mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-24 11:57:59 +01:00
Checkout wot-dijkstra and pgpainless-wot from wot branch
This commit is contained in:
parent
90626a8a76
commit
2a858baee1
51 changed files with 2580 additions and 0 deletions
30
pgpainless-wot/build.gradle
Normal file
30
pgpainless-wot/build.gradle
Normal file
|
@ -0,0 +1,30 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
group 'org.pgpainless'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||
|
||||
// Logging
|
||||
testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
|
||||
|
||||
implementation(project(":pgpainless-core"))
|
||||
|
||||
// Certificate store
|
||||
api "org.pgpainless:pgp-certificate-store:$certDJavaVersion"
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
|
||||
public interface CertificateAuthority {
|
||||
|
||||
/**
|
||||
* Returns <pre>true</pre>, if the given binding (certificate and user-id) is correct.
|
||||
* Correct means, that the binding is trustworthy.
|
||||
*
|
||||
* @param certificate OpenPGP certificate
|
||||
* @param userId user-id
|
||||
* @return binding correctness
|
||||
*/
|
||||
boolean isAuthorized(PGPPublicKeyRing certificate, String userId);
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package org.pgpainless.wot;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import pgp.certificate_store.Certificate;
|
||||
import pgp.certificate_store.exception.BadDataException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class WebOfTrust implements CertificateAuthority {
|
||||
|
||||
private final WebOfTrustCertificateStore certificateStore;
|
||||
|
||||
public WebOfTrust(WebOfTrustCertificateStore certificateStore) {
|
||||
this.certificateStore = certificateStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the heavy lifting of calculating the web of trust.
|
||||
*/
|
||||
public void initialize() throws BadDataException, IOException {
|
||||
Certificate trustRoot = certificateStore.getTrustRoot();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized(PGPPublicKeyRing certificate, String userId) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package org.pgpainless.wot;
|
||||
|
||||
import pgp.certificate_store.Certificate;
|
||||
import pgp.certificate_store.CertificateDirectory;
|
||||
import pgp.certificate_store.MergeCallback;
|
||||
import pgp.certificate_store.exception.BadDataException;
|
||||
import pgp.certificate_store.exception.BadNameException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class WebOfTrustCertificateStore implements CertificateDirectory {
|
||||
|
||||
private final CertificateDirectory certificateDirectory;
|
||||
|
||||
public WebOfTrustCertificateStore(CertificateDirectory certificateDirectory) {
|
||||
this.certificateDirectory = certificateDirectory;
|
||||
}
|
||||
|
||||
public Certificate getTrustRoot() throws BadDataException, IOException {
|
||||
try {
|
||||
return getCertificate("trust-root");
|
||||
} catch (BadNameException e) {
|
||||
throw new AssertionError("The underlying certificate directory MUST support getting a trust-root certificate.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate getCertificate(String identifier)
|
||||
throws IOException, BadNameException, BadDataException {
|
||||
return certificateDirectory.getCertificate(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate getCertificateIfChanged(String identifier, String tag)
|
||||
throws IOException, BadNameException, BadDataException {
|
||||
return certificateDirectory.getCertificateIfChanged(identifier, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate insertCertificate(InputStream data, MergeCallback merge)
|
||||
throws IOException, InterruptedException, BadDataException {
|
||||
return certificateDirectory.insertCertificate(data, merge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate tryInsertCertificate(InputStream data, MergeCallback merge)
|
||||
throws IOException, BadDataException {
|
||||
return certificateDirectory.tryInsertCertificate(data, merge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate insertCertificateBySpecialName(String specialName, InputStream data, MergeCallback merge)
|
||||
throws IOException, InterruptedException, BadDataException, BadNameException {
|
||||
return certificateDirectory.insertCertificateBySpecialName(specialName, data, merge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate tryInsertCertificateBySpecialName(String specialName, InputStream data, MergeCallback merge)
|
||||
throws IOException, BadDataException, BadNameException {
|
||||
return certificateDirectory.tryInsertCertificateBySpecialName(specialName, data, merge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Certificate> getCertificates() {
|
||||
return certificateDirectory.getCertificates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> getFingerprints() {
|
||||
return certificateDirectory.getFingerprints();
|
||||
}
|
||||
}
|
19
wot-dijkstra/QUESTIONS.md
Normal file
19
wot-dijkstra/QUESTIONS.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Questions
|
||||
|
||||
## Graph of Certificates or Graph of Subkeys?
|
||||
|
||||
## Persistable Graph or Dynamic Recalculation?
|
||||
|
||||
https://de.wikipedia.org/wiki/Dijkstra-Algorithmus
|
||||
Dijkstra: Outgoing edges "live" in origin, WoT: Incoming edges "live" in target
|
||||
|
||||
When processing incoming edges on certificate (node): origin of incoming nodes (signing key) might not be known -> cannot verify.
|
||||
|
||||
multi-step process:
|
||||
Firstly create intermediate graph with unverified edges, invert edge such that origin owns edges to targets
|
||||
Secondly for edges where origin and target exist, verify signatures
|
||||
|
||||
What information from signatures to cache? Creation date, expiration? Regex, depth, amount!
|
||||
|
||||
What are then nodes? Certs? Bindings?
|
||||
|
30
wot-dijkstra/build.gradle
Normal file
30
wot-dijkstra/build.gradle
Normal file
|
@ -0,0 +1,30 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
group 'org.pgpainless'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||
|
||||
// Logging
|
||||
testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
|
||||
|
||||
// @Nullable, @Nonnull annotations
|
||||
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
||||
|
||||
implementation(project(":pgpainless-core"))
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
public class Cost {
|
||||
|
||||
public static class SimpleCost extends Cost {
|
||||
|
||||
private final double weight;
|
||||
|
||||
public SimpleCost(double weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
public double getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Double.toString(getWeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof SimpleCost)) {
|
||||
return false;
|
||||
}
|
||||
SimpleCost other = (SimpleCost) obj;
|
||||
return getWeight() == other.getWeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) getWeight();
|
||||
}
|
||||
}
|
||||
|
||||
public static class TrustCost extends Cost {
|
||||
|
||||
private final int depth;
|
||||
private final int amount;
|
||||
private final String regex;
|
||||
|
||||
public TrustCost(int depth, int amount, String regex) {
|
||||
this.depth = depth;
|
||||
this.amount = amount;
|
||||
this.regex = regex;
|
||||
}
|
||||
|
||||
public int getDepth() {
|
||||
return depth;
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public String getRegex() {
|
||||
return regex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "d=" + getDepth() + ",a=" + getAmount() + (regex == null ? "" : ",r=" + getRegex());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class Dijkstra<T, E extends Edge<T, C>, C extends Cost> {
|
||||
@Nullable
|
||||
public abstract Path<T, Node<T>, C, E> findPath(Node<T> to);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
public abstract class Edge<T, C extends Cost> implements Comparable<C> {
|
||||
|
||||
protected final Node<T> from;
|
||||
protected final Node<T> to;
|
||||
protected final C cost;
|
||||
|
||||
public Edge(Node<T> from, Node<T> to, C cost) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
public Node<T> getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public Node<T> getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public C getCost() {
|
||||
return cost;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class Graph<T, N extends Node<T>, E extends Edge<T, C>, C extends Cost> {
|
||||
|
||||
private final Collection<N> nodes;
|
||||
private final Collection<E> edges;
|
||||
|
||||
public Graph(Collection<N> nodes, Collection<E> edges) {
|
||||
this.nodes = nodes;
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public Collection<N> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public Collection<E> getEdges() {
|
||||
return edges;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
public class Node<T> {
|
||||
|
||||
private final T item;
|
||||
|
||||
public Node(T item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
private T getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + getItem().toString() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof Node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node<?> other = (Node<?>) obj;
|
||||
return getItem().equals(other.getItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getItem().hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Path<T, N extends Node<T>, C extends Cost, E extends Edge<T, C>> {
|
||||
|
||||
private final N from;
|
||||
private final N to;
|
||||
|
||||
private final List<E> edges;
|
||||
|
||||
public Path(N from, N to, List<E> edges) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public Node<T> getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public Node<T> getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public List<E> getEdges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Arrays.toString(getEdges().toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof Path)) {
|
||||
return false;
|
||||
}
|
||||
Path<?, ?, ?, ?> other = (Path<?, ?, ?, ?>) obj;
|
||||
return getFrom().equals(other.getFrom())
|
||||
&& getTo().equals(other.getTo())
|
||||
&& getEdges().equals(other.getEdges());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getFrom().hashCode()
|
||||
+ 13 * getTo().hashCode()
|
||||
+ 31 * getEdges().hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ShortestPathDijkstra<T> extends Dijkstra<T, SimpleEdge<T>, Cost.SimpleCost> {
|
||||
|
||||
private final Node<T> root;
|
||||
private final Graph<T, Node<T>, SimpleEdge<T>, Cost.SimpleCost> graph;
|
||||
private final List<Node<T>> queue = new ArrayList<>();
|
||||
|
||||
private final Map<Node<T>, Double> distances = new HashMap<>();
|
||||
private final Map<Node<T>, SimpleEdge<T>> precursors = new HashMap<>();
|
||||
|
||||
public ShortestPathDijkstra(Graph<T, Node<T>, SimpleEdge<T>, Cost.SimpleCost> graph, Node<T> root) {
|
||||
// INITIALIZE
|
||||
this.graph = graph;
|
||||
this.root = root;
|
||||
for (Node<T> node : graph.getNodes()) {
|
||||
// dist[v] := infinity
|
||||
distances.put(node, Double.MAX_VALUE);
|
||||
|
||||
// precursor[v] := null
|
||||
precursors.put(node, null);
|
||||
}
|
||||
// dist[root] := 0
|
||||
distances.put(root, 0d);
|
||||
|
||||
// Q := set of all nodes in graph
|
||||
queue.addAll(graph.getNodes());
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
Node<T> closest = closest();
|
||||
queue.remove(closest);
|
||||
|
||||
for (SimpleEdge<T> edge : graph.getEdges()) {
|
||||
if (!closest.equals(edge.getFrom())) {
|
||||
// Skip non-neighbors
|
||||
continue;
|
||||
}
|
||||
|
||||
if (queue.contains(edge.getTo())) {
|
||||
distUpdate(closest, edge.getTo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Node<T> closest() {
|
||||
Double minDist = Double.MAX_VALUE;
|
||||
int index = 0;
|
||||
Double dist;
|
||||
for (int i = 0; i < queue.size(); i++) {
|
||||
if ((dist = distances.get(queue.get(i))) <= minDist) {
|
||||
index = i;
|
||||
minDist = dist;
|
||||
}
|
||||
}
|
||||
return queue.get(index);
|
||||
}
|
||||
|
||||
private void distUpdate(Node<T> from, Node<T> to) {
|
||||
SimpleEdge<T> edge = getEdgeBetween(from, to);
|
||||
if (edge == null) {
|
||||
// No direct path
|
||||
return;
|
||||
}
|
||||
|
||||
Double distance = distances.get(from) + edge.getCost().getWeight();
|
||||
if (distance < distances.get(to)) {
|
||||
distances.put(to, distance);
|
||||
precursors.put(to, edge);
|
||||
}
|
||||
}
|
||||
|
||||
private SimpleEdge<T> getEdgeBetween(Node<T> from, Node<T> to) {
|
||||
for (SimpleEdge<T> edge : graph.getEdges()) {
|
||||
if (!from.equals(edge.getFrom())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (to.equals(edge.getTo())) {
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Path<T, Node<T>, Cost.SimpleCost, SimpleEdge<T>> findPath(Node<T> to) {
|
||||
List<SimpleEdge<T>> pathEdges = new ArrayList<>();
|
||||
Node<T> waypoint = to;
|
||||
|
||||
SimpleEdge<T> edge;
|
||||
while ((edge = precursors.get(waypoint)) != null) {
|
||||
waypoint = precursors.get(waypoint).getFrom();
|
||||
pathEdges.add(0, edge);
|
||||
}
|
||||
|
||||
if (pathEdges.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Path<>(root, to, pathEdges);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
public class SimpleEdge<T> extends Edge<T, Cost.SimpleCost> {
|
||||
|
||||
public SimpleEdge(Node<T> from, Node<T> to, Double edgeWeight) {
|
||||
super(from, to, new Cost.SimpleCost(edgeWeight));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getFrom().toString() + " " + getCost() + "> " + getTo().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Cost.SimpleCost o) {
|
||||
return Double.compare(getCost().getWeight(), o.getWeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof SimpleEdge)) {
|
||||
return false;
|
||||
}
|
||||
SimpleEdge<?> other = (SimpleEdge<?>) obj;
|
||||
|
||||
return getFrom().equals(other.getFrom())
|
||||
&& getTo().equals(other.getTo())
|
||||
&& getCost().equals(other.getCost());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getFrom().hashCode() + 13 * getTo().hashCode() + 17 * getCost().hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class TrustEdge<T> extends Edge<T, Cost.TrustCost> {
|
||||
|
||||
public TrustEdge(Node<T> from, Node<T> to, Cost.TrustCost cost) {
|
||||
super(from, to, cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@Nonnull Cost.TrustCost o) {
|
||||
int depthCompare = Double.compare(cost.getDepth(), o.getDepth());
|
||||
if (depthCompare != 0) {
|
||||
return - depthCompare;
|
||||
}
|
||||
return Double.compare(cost.getAmount(), o.getAmount());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class WotDijkstra<T> extends Dijkstra<T, TrustEdge<T>, Cost.TrustCost> {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Path<T, Node<T>, Cost.TrustCost, TrustEdge<T>> findPath(Node<T> to) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import org.pgpainless.algorithm.RevocationState;
|
||||
import org.pgpainless.key.OpenPgpFingerprint;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class CertSynopsis {
|
||||
|
||||
private final OpenPgpFingerprint fingerprint;
|
||||
private final Date expirationTime;
|
||||
private final RevocationState revocationState;
|
||||
private final Set<String> userIds;
|
||||
|
||||
/**
|
||||
* Create a new {@link CertSynopsis}.
|
||||
*
|
||||
* @param fingerprint fingerprint of the certificate
|
||||
* @param expirationTime expiration time
|
||||
* @param revocationState revocation state of the certificate
|
||||
* @param userIds set of user-ids
|
||||
*/
|
||||
public CertSynopsis(OpenPgpFingerprint fingerprint,
|
||||
Date expirationTime,
|
||||
RevocationState revocationState,
|
||||
Set<String> userIds) {
|
||||
this.fingerprint = fingerprint;
|
||||
this.expirationTime = expirationTime;
|
||||
this.revocationState = revocationState;
|
||||
this.userIds = userIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the certificate.
|
||||
*
|
||||
* @return fingerprint
|
||||
*/
|
||||
public OpenPgpFingerprint getFingerprint() {
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the certificates expiration time.
|
||||
*
|
||||
* @return expiration time
|
||||
*/
|
||||
public Date getExpirationTime() {
|
||||
return expirationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the revocation status of the certificate.
|
||||
*
|
||||
* @return revocation state
|
||||
*/
|
||||
public RevocationState getRevocationState() {
|
||||
return revocationState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link Set} containing all user-ids of the certificate.
|
||||
*
|
||||
* @return user-ids
|
||||
*/
|
||||
public Set<String> userIds() {
|
||||
return new HashSet<>(userIds);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
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 {
|
||||
|
||||
private final CertSynopsis issuer;
|
||||
private final CertSynopsis target;
|
||||
private final Optional<String> userId;
|
||||
|
||||
private final Date creationTime;
|
||||
private final Optional<Date> expirationTime;
|
||||
private final boolean exportable;
|
||||
private final int trustAmount;
|
||||
private final Depth trustDepth;
|
||||
private final RegexSet regex;
|
||||
|
||||
public Certification(
|
||||
CertSynopsis issuer,
|
||||
CertSynopsis target,
|
||||
Optional<String> userId,
|
||||
Date creationTime,
|
||||
Optional<Date> expirationTime,
|
||||
boolean exportable,
|
||||
int trustAmount,
|
||||
Depth trustDepth,
|
||||
RegexSet regex) {
|
||||
this.issuer = issuer;
|
||||
this.target = target;
|
||||
this.userId = userId;
|
||||
this.creationTime = creationTime;
|
||||
this.expirationTime = expirationTime;
|
||||
this.exportable = exportable;
|
||||
this.trustAmount= trustAmount;
|
||||
this.trustDepth = trustDepth;
|
||||
this.regex = regex;
|
||||
}
|
||||
|
||||
public Certification(CertSynopsis issuer,
|
||||
Optional<String> targetUserId,
|
||||
CertSynopsis target,
|
||||
Date creationTime) {
|
||||
this.issuer = issuer;
|
||||
this.target = target;
|
||||
this.userId = targetUserId;
|
||||
this.creationTime = creationTime;
|
||||
|
||||
this.expirationTime = Optional.empty();
|
||||
this.exportable = true;
|
||||
this.trustDepth = Depth.limited(0);
|
||||
this.trustAmount = 120;
|
||||
this.regex = RegexSet.wildcard();
|
||||
}
|
||||
|
||||
public Certification(CertSynopsis issuer,
|
||||
Optional<String> 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<RegularExpression> regularExpressionList = SignatureSubpacketsUtil.getRegularExpressions(signature);
|
||||
this.regex = RegexSet.fromList(regularExpressionList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the issuer of the certification.
|
||||
*
|
||||
* @return issuer
|
||||
*/
|
||||
public CertSynopsis getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target of the certification.
|
||||
*
|
||||
* @return target
|
||||
*/
|
||||
public CertSynopsis getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target user-id.
|
||||
*
|
||||
* @return user-id
|
||||
*/
|
||||
public Optional<String> getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the creation time of the certification,
|
||||
*
|
||||
* @return creation time
|
||||
*/
|
||||
public Date getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the (optional) expiration time of the certification.
|
||||
*
|
||||
* @return optional expiration time
|
||||
*/
|
||||
public Optional<Date> getExpirationTime() {
|
||||
return expirationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the certification is marked as exportable.
|
||||
*
|
||||
* @return exportable
|
||||
*/
|
||||
public boolean isExportable() {
|
||||
return exportable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the trust amount of the certification.
|
||||
*
|
||||
* @return trust amount
|
||||
*/
|
||||
public int getTrustAmount() {
|
||||
return trustAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the trust depth of the certification.
|
||||
*
|
||||
* @return trust depth
|
||||
*/
|
||||
public Depth getTrustDepth() {
|
||||
return trustDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of regular expressions.
|
||||
*
|
||||
* @return regex set
|
||||
*/
|
||||
public RegexSet getRegexes() {
|
||||
return regex;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class CertificationSet {
|
||||
|
||||
private final CertSynopsis issuer;
|
||||
private final CertSynopsis target;
|
||||
|
||||
private final Map<Optional<String>, List<Certification>> certifications;
|
||||
|
||||
/**
|
||||
* Create an empty {@link CertificationSet}.
|
||||
*
|
||||
* @param issuer issuer
|
||||
* @param target target
|
||||
* @return empty set
|
||||
*/
|
||||
public static CertificationSet empty(CertSynopsis issuer, CertSynopsis target) {
|
||||
return new CertificationSet(issuer, target, new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String> userId,
|
||||
PGPSignature certification) {
|
||||
|
||||
Map<Optional<String>, List<Certification>> certificationMap = new HashMap<>();
|
||||
List<Certification> 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<Optional<String>, List<Certification>> certifications) {
|
||||
this.issuer = issuer;
|
||||
this.target = target;
|
||||
this.certifications = new HashMap<>(certifications);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this {@link CertificationSet} with another instance.
|
||||
* After the operation, this will contain {@link Certification Certifications} from both sets.
|
||||
*
|
||||
* @param other other {@link CertificationSet}
|
||||
*/
|
||||
public void merge(@Nonnull CertificationSet other) {
|
||||
if (other == this) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!issuer.getFingerprint().equals(other.issuer.getFingerprint())) {
|
||||
throw new IllegalArgumentException("Issuer fingerprint mismatch.");
|
||||
}
|
||||
|
||||
if (!target.getFingerprint().equals(other.target.getFingerprint())) {
|
||||
throw new IllegalArgumentException("Target fingerprint mismatch.");
|
||||
}
|
||||
|
||||
for (Map.Entry<Optional<String>, List<Certification>> entry : other.certifications.entrySet()) {
|
||||
for (Certification certification : entry.getValue()) {
|
||||
add(certification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link Certification} into this {@link CertificationSet}.
|
||||
*
|
||||
* @param certification certification
|
||||
*/
|
||||
public void add(@Nonnull Certification certification) {
|
||||
if (!issuer.getFingerprint().equals(certification.getIssuer().getFingerprint())) {
|
||||
throw new IllegalArgumentException("Issuer fingerprint mismatch.");
|
||||
}
|
||||
if (!target.getFingerprint().equals(certification.getTarget().getFingerprint())) {
|
||||
throw new IllegalArgumentException("Target fingerprint mismatch.");
|
||||
}
|
||||
|
||||
List<Certification> certificationsForUserId = certifications.get(certification.getUserId());
|
||||
//noinspection Java8MapApi
|
||||
if (certificationsForUserId == null) {
|
||||
certificationsForUserId = new ArrayList<>();
|
||||
certifications.put(certification.getUserId(), certificationsForUserId);
|
||||
}
|
||||
// TODO: Prevent duplicates, only keep newest timestamped sig?
|
||||
certificationsForUserId.add(certification);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class Depth implements Comparable<Depth> {
|
||||
|
||||
private final Optional<Integer> depth;
|
||||
|
||||
private Depth(Optional<Integer> depth) {
|
||||
this.depth = depth;
|
||||
if (!isUnconstrained() && (getLimit().get() < 0 || getLimit().get() > 255)) {
|
||||
throw new IllegalArgumentException("Depth must be a value between 0 and 255");
|
||||
}
|
||||
}
|
||||
|
||||
public static Depth unconstrained() {
|
||||
return new Depth(Optional.empty());
|
||||
}
|
||||
|
||||
public static Depth limited(int depth) {
|
||||
return new Depth(Optional.just(depth));
|
||||
}
|
||||
|
||||
public static Depth auto(int depth) {
|
||||
return depth == 255 ? unconstrained() : limited(depth);
|
||||
}
|
||||
|
||||
public Optional<Integer> getLimit() {
|
||||
return depth;
|
||||
}
|
||||
|
||||
public boolean isUnconstrained() {
|
||||
return getLimit().isEmpty();
|
||||
}
|
||||
|
||||
public Depth decrease(int value) {
|
||||
if (isUnconstrained()) {
|
||||
return unconstrained();
|
||||
}
|
||||
if (getLimit().get() >= value) {
|
||||
return limited(getLimit().get() - value);
|
||||
}
|
||||
throw new IllegalArgumentException("Depth cannot be decreased.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@Nonnull Depth o) {
|
||||
if (isUnconstrained()) {
|
||||
if (o.isUnconstrained()) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (o.isUnconstrained()) {
|
||||
return -1;
|
||||
} else {
|
||||
return Integer.compare(getLimit().get(), o.getLimit().get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return isUnconstrained() ? "unconstrained" : getLimit().get().toString();
|
||||
}
|
||||
|
||||
public Depth min(Depth other) {
|
||||
if (compareTo(other) <= 0) {
|
||||
return this;
|
||||
} else {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.bcpg.sig.RevocationReason;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.pgpainless.algorithm.KeyFlag;
|
||||
import org.pgpainless.algorithm.RevocationState;
|
||||
import org.pgpainless.algorithm.RevocationStateType;
|
||||
import org.pgpainless.key.OpenPgpFingerprint;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
import org.pgpainless.key.util.RevocationAttributes;
|
||||
import org.pgpainless.policy.Policy;
|
||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
||||
|
||||
public class Network {
|
||||
|
||||
private final Map<OpenPgpFingerprint, CertSynopsis> nodes;
|
||||
private final Map<OpenPgpFingerprint, List<CertificationSet>> edges;
|
||||
private final Map<OpenPgpFingerprint, List<CertificationSet>> reverseEdges;
|
||||
private final ReferenceTime referenceTime;
|
||||
|
||||
public Network(Map<OpenPgpFingerprint, CertSynopsis> nodes,
|
||||
Map<OpenPgpFingerprint, List<CertificationSet>> edges,
|
||||
Map<OpenPgpFingerprint, List<CertificationSet>> reverseEdges,
|
||||
ReferenceTime referenceTime) {
|
||||
this.nodes = nodes;
|
||||
this.edges = edges;
|
||||
this.reverseEdges = reverseEdges;
|
||||
this.referenceTime = referenceTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty {@link Network}.
|
||||
*
|
||||
* @param referenceTime reference time for evaluation
|
||||
* @return network
|
||||
*/
|
||||
public static Network empty(@Nonnull ReferenceTime referenceTime) {
|
||||
return new Network(
|
||||
new HashMap<>(),
|
||||
new HashMap<>(),
|
||||
new HashMap<>(),
|
||||
referenceTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Network} from a set of certificates.
|
||||
*
|
||||
* @param certificates set of certificates
|
||||
* @param policy evaluation policy
|
||||
* @param optReferenceTime reference time for evaluation
|
||||
* @return network
|
||||
*/
|
||||
public static Network fromCertificates(
|
||||
Iterable<PGPPublicKeyRing> certificates,
|
||||
Policy policy,
|
||||
Optional<ReferenceTime> optReferenceTime) {
|
||||
ReferenceTime referenceTime = optReferenceTime.isPresent() ? optReferenceTime.get() : ReferenceTime.now();
|
||||
List<KeyRingInfo> validCerts = new ArrayList<>();
|
||||
for (PGPPublicKeyRing cert : certificates) {
|
||||
KeyRingInfo info = new KeyRingInfo(cert, policy, referenceTime.getTimestamp());
|
||||
if (info.getValidUserIds().isEmpty()) {
|
||||
// Ignore invalid cert
|
||||
} else {
|
||||
validCerts.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
return fromValidCertificates(
|
||||
validCerts,
|
||||
referenceTime
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Network} from a set of validated certificates.
|
||||
*
|
||||
* @param validatedCertificates set of validated certificates
|
||||
* @param referenceTime reference time
|
||||
* @return network
|
||||
*/
|
||||
public static Network fromValidCertificates(
|
||||
Iterable<KeyRingInfo> validatedCertificates,
|
||||
ReferenceTime referenceTime) {
|
||||
|
||||
Map<OpenPgpFingerprint, KeyRingInfo> byFingerprint = new HashMap<>();
|
||||
Map<Long, List<KeyRingInfo>> byKeyId = new HashMap<>();
|
||||
|
||||
Map<OpenPgpFingerprint, CertSynopsis> certSynopsisMap = new HashMap<>();
|
||||
|
||||
for (KeyRingInfo cert : validatedCertificates) {
|
||||
//noinspection Java8MapApi
|
||||
if (byFingerprint.get(cert.getFingerprint()) == null) {
|
||||
byFingerprint.put(cert.getFingerprint(), cert);
|
||||
}
|
||||
List<KeyRingInfo> byKeyIdEntry = byKeyId.get(cert.getKeyId());
|
||||
|
||||
//noinspection Java8MapApi
|
||||
if (byKeyIdEntry == null) {
|
||||
byKeyIdEntry = new ArrayList<>();
|
||||
byKeyId.put(cert.getKeyId(), byKeyIdEntry);
|
||||
}
|
||||
byKeyIdEntry.add(cert);
|
||||
|
||||
certSynopsisMap.put(cert.getFingerprint(),
|
||||
new CertSynopsis(cert.getFingerprint(),
|
||||
cert.getExpirationDateForUse(KeyFlag.CERTIFY_OTHER),
|
||||
revocationStateFromSignature(cert.getRevocationSelfSignature()),
|
||||
new HashSet<>(cert.getValidUserIds())));
|
||||
}
|
||||
|
||||
Map<OpenPgpFingerprint, List<CertificationSet>> edges = new HashMap<>();
|
||||
Map<OpenPgpFingerprint, List<CertificationSet>> reverseEdges = new HashMap<>();
|
||||
|
||||
return new Network(certSynopsisMap, edges, reverseEdges, referenceTime);
|
||||
}
|
||||
|
||||
public Map<OpenPgpFingerprint, CertSynopsis> getNodes() {
|
||||
return new HashMap<>(nodes);
|
||||
}
|
||||
|
||||
public Map<OpenPgpFingerprint, List<CertificationSet>> getEdges() {
|
||||
return new HashMap<>(edges);
|
||||
}
|
||||
|
||||
public Map<OpenPgpFingerprint, List<CertificationSet>> getReverseEdges() {
|
||||
return new HashMap<>(reverseEdges);
|
||||
}
|
||||
|
||||
public ReferenceTime getReferenceTime() {
|
||||
return referenceTime;
|
||||
}
|
||||
|
||||
private static RevocationState revocationStateFromSignature(PGPSignature revocation) {
|
||||
if (revocation == null) {
|
||||
return RevocationState.notRevoked();
|
||||
}
|
||||
|
||||
RevocationReason revocationReason = SignatureSubpacketsUtil.getRevocationReason(revocation);
|
||||
if (revocationReason == null) {
|
||||
return RevocationState.hardRevoked();
|
||||
}
|
||||
|
||||
return RevocationAttributes.Reason.isHardRevocation(revocationReason.getRevocationReason()) ?
|
||||
RevocationState.hardRevoked() : RevocationState.softRevoked(revocation.getCreationTime());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class Optional<T> {
|
||||
|
||||
private final T item;
|
||||
|
||||
public static <T> Optional<T> empty() {
|
||||
return new Optional<>();
|
||||
}
|
||||
|
||||
public static <T> Optional<T> just(@Nonnull T item) {
|
||||
return new Optional<>(item);
|
||||
}
|
||||
|
||||
public static <T> Optional<T> maybe(@Nullable T item) {
|
||||
return item == null ? empty() : just(item);
|
||||
}
|
||||
|
||||
private Optional() {
|
||||
this.item = null;
|
||||
}
|
||||
|
||||
private Optional(@Nonnull T item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return item == null;
|
||||
}
|
||||
|
||||
public boolean isPresent() {
|
||||
return item != null;
|
||||
}
|
||||
|
||||
public @Nonnull T get() {
|
||||
if (item == null) {
|
||||
throw new NullPointerException("Item is null.");
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof Optional)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional other = (Optional) obj;
|
||||
if (isEmpty() && other.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isPresent() && isPresent()) {
|
||||
return get().equals(other.get());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Path {
|
||||
|
||||
private CertSynopsis root;
|
||||
private List<Certification> edges;
|
||||
private Depth residualDepth;
|
||||
|
||||
public Path(CertSynopsis root) {
|
||||
this.root = root;
|
||||
this.edges = new ArrayList<>();
|
||||
this.residualDepth = Depth.unconstrained();
|
||||
}
|
||||
|
||||
public CertSynopsis getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public CertSynopsis getTarget() {
|
||||
if (edges.isEmpty()) {
|
||||
return getRoot();
|
||||
} else {
|
||||
return edges.get(edges.size() - 1).getTarget();
|
||||
}
|
||||
}
|
||||
|
||||
public List<CertSynopsis> getCertificates() {
|
||||
List<CertSynopsis> certs = new ArrayList<>();
|
||||
certs.add(getRoot());
|
||||
for (Certification edge : edges) {
|
||||
certs.add(edge.getTarget());
|
||||
}
|
||||
return certs;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return edges.size() + 1;
|
||||
}
|
||||
|
||||
public List<Certification> getCertifications() {
|
||||
return new ArrayList<>(edges);
|
||||
}
|
||||
|
||||
public Depth getResidualDepth() {
|
||||
return residualDepth;
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
if (edges.isEmpty()) {
|
||||
return 120;
|
||||
}
|
||||
int min = 255;
|
||||
for (Certification edge : edges) {
|
||||
min = Math.min(min, edge.getTrustAmount());
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
public void append(Certification certification) {
|
||||
if (!getTarget().getFingerprint().equals(certification.getIssuer().getFingerprint())) {
|
||||
throw new IllegalArgumentException("Cannot append certification to path: Path's tail is not issuer of the certification.");
|
||||
}
|
||||
|
||||
if (!residualDepth.isUnconstrained() && residualDepth.getLimit().get() == 0) {
|
||||
throw new IllegalArgumentException("Not enough depth.");
|
||||
}
|
||||
|
||||
boolean cyclic = getRoot().getFingerprint().equals(certification.getTarget().getFingerprint());
|
||||
for (int i = 0; i < edges.size() && !cyclic; i++) {
|
||||
Certification edge = edges.get(i);
|
||||
|
||||
if (edge.getTarget().getFingerprint().equals(certification.getTarget().getFingerprint())) {
|
||||
if (i == edges.size() - 1) {
|
||||
cyclic = edge.getUserId().equals(certification.getUserId());
|
||||
} else {
|
||||
cyclic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cyclic) {
|
||||
throw new IllegalArgumentException("Adding the certification to the path would create a cycle.");
|
||||
}
|
||||
|
||||
residualDepth = certification.getTrustDepth().min(residualDepth.decrease(1));
|
||||
edges.add(certification);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Paths {
|
||||
|
||||
private final List<Item> paths = new ArrayList<>();
|
||||
|
||||
public List<Item> getPaths() {
|
||||
return new ArrayList<>(paths);
|
||||
}
|
||||
|
||||
public void add(Path path, int amount) {
|
||||
if (amount <= path.getAmount()) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
this.paths.add(new Item(path, amount));
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
int sum = 0;
|
||||
for (Item item : paths) {
|
||||
sum += item.amount;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static class Item {
|
||||
private final Path path;
|
||||
private final int amount;
|
||||
|
||||
public Item(Path path, int amount) {
|
||||
this.path = path;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Date;
|
||||
|
||||
public interface ReferenceTime {
|
||||
|
||||
@Nonnull Date getTimestamp();
|
||||
|
||||
static ReferenceTime now() {
|
||||
final Date now = new Date();
|
||||
return new ReferenceTime() {
|
||||
@Override
|
||||
@Nonnull
|
||||
public Date getTimestamp() {
|
||||
return now;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static ReferenceTime timestamp(@Nonnull Date timestamp) {
|
||||
return new ReferenceTime() {
|
||||
@Override
|
||||
@Nonnull
|
||||
public Date getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.bcpg.sig.RegularExpression;
|
||||
|
||||
public class RegexSet {
|
||||
|
||||
private final Set<String> regexStrings;
|
||||
|
||||
private RegexSet(Set<String> regexStrings) {
|
||||
this.regexStrings = regexStrings;
|
||||
}
|
||||
|
||||
public static RegexSet fromList(@Nonnull List<RegularExpression> regexList) {
|
||||
Set<String> regexStringSet = new HashSet<>();
|
||||
for (RegularExpression regex : regexList) {
|
||||
regexStringSet.add(regex.getRegex());
|
||||
}
|
||||
return new RegexSet(regexStringSet);
|
||||
}
|
||||
|
||||
public static RegexSet fromRegex(@Nonnull RegularExpression regex) {
|
||||
return fromList(Collections.singletonList(regex));
|
||||
}
|
||||
|
||||
public static RegexSet wildcard() {
|
||||
return fromList(Collections.emptyList());
|
||||
}
|
||||
|
||||
public boolean matches(String string) {
|
||||
if (regexStrings.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (String regex : regexStrings) {
|
||||
Matcher matcher = Pattern.compile(regex).matcher(string);
|
||||
if (matcher.matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class BasicShortestPathDijkstraTest {
|
||||
|
||||
/**
|
||||
* Generate a test graph from a string definition.
|
||||
* The definition might look like this:
|
||||
* <pre>
|
||||
* Alice
|
||||
* Bob -1> Charlie
|
||||
* Charlie -4> Dieter -1> Alice
|
||||
* Dieter -2> Charlie
|
||||
* </pre>
|
||||
* @param definition definition
|
||||
* @return graph
|
||||
*/
|
||||
private Graph<String, Node<String>, SimpleEdge<String>, Cost.SimpleCost> generate(String definition) {
|
||||
Set<Node<String>> nodes = new HashSet<>();
|
||||
Set<SimpleEdge<String>> edges = new HashSet<>();
|
||||
String[] lines = definition.split("\n");
|
||||
for (String line : lines) {
|
||||
line = line.trim();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] fromTo = line.split(" -\\d+> ");
|
||||
if (fromTo.length == 1) {
|
||||
// Unconnected node
|
||||
nodes.add(new Node<>(fromTo[0]));
|
||||
continue;
|
||||
}
|
||||
|
||||
int searchOffset = 0;
|
||||
for (int i = 0; i < fromTo.length - 1; i++) {
|
||||
Node<String> from = new Node<>(fromTo[i]);
|
||||
nodes.add(from);
|
||||
searchOffset += fromTo[i].length() + " -".length();
|
||||
int costStop = line.indexOf("> ", searchOffset);
|
||||
String costString = line.substring(searchOffset, costStop);
|
||||
Double cost = Double.parseDouble(costString);
|
||||
searchOffset += costString.length() + "> ".length();
|
||||
Node<String> to = new Node<>(fromTo[i + 1]);
|
||||
nodes.add(to);
|
||||
|
||||
edges.add(new SimpleEdge<>(from, to, cost));
|
||||
}
|
||||
}
|
||||
|
||||
return new Graph<>(nodes, edges);
|
||||
}
|
||||
|
||||
private Path<String, Node<String>, Cost.SimpleCost, SimpleEdge<String>> path(String definition) {
|
||||
definition = definition.trim();
|
||||
String[] fromTo = definition.split(" -\\d+> ");
|
||||
if (fromTo.length == 1) {
|
||||
// Unconnected node
|
||||
Node<String> node = new Node<>(fromTo[0]);
|
||||
return new Path<>(node, node, Collections.singletonList(new SimpleEdge<>(node, node, 0d)));
|
||||
}
|
||||
|
||||
Node<String> start = null;
|
||||
Node<String> end = null;
|
||||
List<SimpleEdge<String>> edges = new ArrayList<>();
|
||||
int searchOffset = 0;
|
||||
for (int i = 0; i < fromTo.length - 1; i++) {
|
||||
Node<String> from = new Node<>(fromTo[i]);
|
||||
if (start == null) {
|
||||
start = from;
|
||||
}
|
||||
searchOffset += fromTo[i].length() + " -".length();
|
||||
int costStop = definition.indexOf("> ", searchOffset);
|
||||
String costString = definition.substring(searchOffset, costStop);
|
||||
Double cost = Double.parseDouble(costString);
|
||||
searchOffset += costString.length() + "> ".length();
|
||||
Node<String> to = new Node<>(fromTo[i + 1]);
|
||||
edges.add(new SimpleEdge<>(from, to, cost));
|
||||
end = to;
|
||||
}
|
||||
|
||||
Path<String, Node<String>, Cost.SimpleCost, SimpleEdge<String>> path = new Path<>(start, end, edges);
|
||||
return path;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exampleGraphTest() {
|
||||
Graph<String, Node<String>, SimpleEdge<String>, Cost.SimpleCost> g = generate(
|
||||
"Alice\n" +
|
||||
"Bob -1> Charlie\n" +
|
||||
"Bob -3> Dieter -1> Marlene\n" +
|
||||
"Dieter -1> Alice\n" +
|
||||
"Mallory\n");
|
||||
|
||||
Set<Node<String>> expectedNodes = new HashSet<>();
|
||||
expectedNodes.add(new Node<>("Alice"));
|
||||
expectedNodes.add(new Node<>("Bob"));
|
||||
expectedNodes.add(new Node<>("Charlie"));
|
||||
expectedNodes.add(new Node<>("Dieter"));
|
||||
expectedNodes.add(new Node<>("Marlene"));
|
||||
expectedNodes.add(new Node<>("Mallory"));
|
||||
|
||||
assertEquals(expectedNodes, g.getNodes());
|
||||
|
||||
Set<SimpleEdge<String>> expectedEdges = new HashSet<>();
|
||||
expectedEdges.add(new SimpleEdge<>(new Node<>("Bob"), new Node<>("Charlie"), 1d));
|
||||
expectedEdges.add(new SimpleEdge<>(new Node<>("Bob"), new Node<>("Dieter"), 3d));
|
||||
expectedEdges.add(new SimpleEdge<>(new Node<>("Dieter"), new Node<>("Marlene"), 1d));
|
||||
expectedEdges.add(new SimpleEdge<>(new Node<>("Dieter"), new Node<>("Alice"), 1d));
|
||||
|
||||
assertEquals(g.getEdges(), expectedEdges);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyNetworkTest() {
|
||||
Graph<String, Node<String>, SimpleEdge<String>, Cost.SimpleCost> graph = generate("");
|
||||
|
||||
Node<String> root = new Node<>("root");
|
||||
Node<String> target = new Node<>("target");
|
||||
ShortestPathDijkstra<String> dijkstra = new ShortestPathDijkstra<>(graph, root);
|
||||
Path<String, Node<String>, Cost.SimpleCost, SimpleEdge<String>> path = dijkstra.findPath(target);
|
||||
|
||||
assertNull(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathFindingTest() {
|
||||
Graph<String, Node<String>, SimpleEdge<String>, Cost.SimpleCost> graph = generate(
|
||||
"Pablo\n" +
|
||||
"Root -2> Alice -3> Alexandra\n" +
|
||||
"Root -1> Karlos -1> Alexandra\n" +
|
||||
"Karlos -2> Malte -4> Sven");
|
||||
|
||||
ShortestPathDijkstra<String> dijkstra = new ShortestPathDijkstra<>(graph, new Node<>("Root"));
|
||||
assertEquals(path("Root -1> Karlos -2> Malte -4> Sven"), dijkstra.findPath(new Node<>("Sven")));
|
||||
assertEquals(path("Root -1> Karlos"), dijkstra.findPath(new Node<>("Karlos")));
|
||||
assertEquals(path("Root -1> Karlos -1> Alexandra"), dijkstra.findPath(new Node<>("Alexandra")));
|
||||
|
||||
dijkstra = new ShortestPathDijkstra<>(graph, new Node<>("Karlos"));
|
||||
assertEquals(path("Karlos -2> Malte -4> Sven"), dijkstra.findPath(new Node<>("Sven")));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
public class NodeTest {
|
||||
|
||||
@Test
|
||||
public void equalsTest() {
|
||||
Node<String> n1 = new Node<>("foo");
|
||||
Node<String> n1_ = new Node<>("foo");
|
||||
Node<String> n2 = new Node<>("bar");
|
||||
|
||||
assertEquals(n1, n1_);
|
||||
assertEquals(n1, n1);
|
||||
assertNotEquals(n1, n2);
|
||||
|
||||
Map<Node<String>, String> map = new HashMap<>();
|
||||
map.put(n1, "foo");
|
||||
map.put(n2, "bar");
|
||||
|
||||
assertEquals("foo", map.get(n1));
|
||||
assertEquals("bar", map.get(n2));
|
||||
assertEquals("foo", map.get(n1_));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class DepthTest {
|
||||
|
||||
@Test
|
||||
public void testUnlimitedItem() {
|
||||
Depth depth = Depth.unconstrained();
|
||||
assertTrue(depth.isUnconstrained());
|
||||
assertFalse(depth.getLimit().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLimitedItem() {
|
||||
Depth limited = Depth.limited(2);
|
||||
assertFalse(limited.isUnconstrained());
|
||||
assertTrue(limited.getLimit().isPresent());
|
||||
assertEquals(2, limited.getLimit().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecreaseUnconstrainedYieldsUnconstrained() {
|
||||
Depth unconstrained = Depth.unconstrained();
|
||||
Depth decreased = unconstrained.decrease(20);
|
||||
assertTrue(decreased.isUnconstrained());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecreaseLimitedYieldsDecreasedLimited() {
|
||||
Depth limited = Depth.limited(1);
|
||||
Depth decreased = limited.decrease(1);
|
||||
assertFalse(decreased.isUnconstrained());
|
||||
assertEquals(0, decreased.getLimit().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecreaseLimitedTooMuchYieldsException() {
|
||||
Depth limited = Depth.limited(0);
|
||||
assertThrows(IllegalArgumentException.class, () -> limited.decrease(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareTo() {
|
||||
Depth unlimited = Depth.unconstrained();
|
||||
Depth unlimited2 = Depth.unconstrained();
|
||||
Depth depth2 = Depth.limited(2);
|
||||
Depth depth2_ = Depth.limited(2);
|
||||
Depth depth5 = Depth.limited(5);
|
||||
|
||||
assertEquals(0, unlimited.compareTo(unlimited2));
|
||||
assertTrue(unlimited.compareTo(depth2) > 0);
|
||||
assertTrue(unlimited.compareTo(depth5) > 0);
|
||||
assertTrue(depth2.compareTo(unlimited) < 0);
|
||||
assertTrue(depth2.compareTo(depth5) < 0);
|
||||
assertTrue(depth5.compareTo(depth2) > 0);
|
||||
assertEquals(0, depth2.compareTo(depth2_));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoUnconstrained() {
|
||||
Depth depth = Depth.auto(255);
|
||||
assertTrue(depth.isUnconstrained());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoLimited() {
|
||||
Depth depth = Depth.auto(120);
|
||||
assertFalse(depth.isUnconstrained());
|
||||
assertEquals(120, depth.getLimit().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutOfBounds() {
|
||||
assertThrows(IllegalArgumentException.class, () -> Depth.limited(-1));
|
||||
assertThrows(IllegalArgumentException.class, () -> Depth.limited(256));
|
||||
assertThrows(IllegalArgumentException.class, () -> Depth.auto(-1));
|
||||
assertThrows(IllegalArgumentException.class, () -> Depth.auto(256));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToStringUnconstrained() {
|
||||
assertEquals("unconstrained", Depth.unconstrained().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToStringLimited() {
|
||||
assertEquals("1", Depth.limited(1).toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class NetworkTest {
|
||||
|
||||
@Test
|
||||
public void testEmptyNetworkIsEmpty() {
|
||||
ReferenceTime referenceTime = ReferenceTime.now();
|
||||
Network network = Network.empty(referenceTime);
|
||||
|
||||
assertTrue(network.getNodes().isEmpty());
|
||||
assertTrue(network.getEdges().isEmpty());
|
||||
assertTrue(network.getReverseEdges().isEmpty());
|
||||
assertEquals(referenceTime, network.getReferenceTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNetworkFromCertificates() {
|
||||
ReferenceTime referenceTime = ReferenceTime.now();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.algorithm.Trustworthiness;
|
||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||
import org.pgpainless.signature.subpackets.CertificationSubpackets;
|
||||
import org.pgpainless.util.Passphrase;
|
||||
|
||||
public class WotTestVectors {
|
||||
|
||||
private static WotTestVectors INSTANCE = null;
|
||||
|
||||
public static WotTestVectors getTestVectors() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new WotTestVectors();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public PGPSecretKeyRing getFreshFooBankCaKey() throws IOException {
|
||||
return PGPainless.readKeyRing().secretKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/foobankCaKey.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getFreshFooBankCaCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/foobankCaCert.asc"));
|
||||
}
|
||||
|
||||
public String getFooBankCaPassphrase() {
|
||||
return "superS3cureP4ssphrase";
|
||||
}
|
||||
|
||||
public SecretKeyRingProtector getFooBankCaProtector() {
|
||||
return SecretKeyRingProtector.unlockAnyKeyWith(Passphrase.fromPassword(getFooBankCaPassphrase()));
|
||||
}
|
||||
|
||||
public PGPSecretKeyRing getFreshFooBankEmployeeKey() throws IOException {
|
||||
return PGPainless.readKeyRing().secretKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/foobankEmployeeKey.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getFreshFooBankEmployeeCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/foobankEmployeeCert.asc"));
|
||||
}
|
||||
|
||||
public String getFooBankEmployeePassphrase() {
|
||||
return "iLoveWorking@FooBank";
|
||||
}
|
||||
|
||||
public SecretKeyRingProtector getFooBankEmployeeProtector() {
|
||||
return SecretKeyRingProtector.unlockAnyKeyWith(Passphrase.fromPassword(getFooBankEmployeePassphrase()));
|
||||
}
|
||||
|
||||
public PGPSecretKeyRing getFreshFooBankAdminKey() throws IOException {
|
||||
return PGPainless.readKeyRing().secretKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/foobankAdminKey.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getFreshFooBankAdminCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/foobankAdminCert.asc"));
|
||||
}
|
||||
|
||||
public String getFooBankAdminPassphrase() {
|
||||
return "keepFooBankSecure";
|
||||
}
|
||||
|
||||
public SecretKeyRingProtector getFooBankAdminProtector() {
|
||||
return SecretKeyRingProtector.unlockAnyKeyWith(Passphrase.fromPassword(getFooBankAdminPassphrase()));
|
||||
}
|
||||
|
||||
public PGPSecretKeyRing getFreshFooBankCustomerKey() throws IOException {
|
||||
return PGPainless.readKeyRing().secretKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/foobankCustomerKey.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getFreshFooBankCustomerCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/foobankCustomerCert.asc"));
|
||||
}
|
||||
|
||||
public PGPSecretKeyRing getFreshBarBankCaKey() throws IOException {
|
||||
return PGPainless.readKeyRing().secretKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/barbankCaKey.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getFreshBarBankCaCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/barbankCaCert.asc"));
|
||||
}
|
||||
|
||||
public PGPSecretKeyRing getFreshBarBankEmployeeKey() throws IOException {
|
||||
return PGPainless.readKeyRing().secretKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/barbankEmployeeKey.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getFreshBarBankEmployeeCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/barbankEmployeeCert.asc"));
|
||||
}
|
||||
|
||||
public PGPSecretKeyRing getFreshFakeFooBankEmployeeKey() throws IOException {
|
||||
return PGPainless.readKeyRing().secretKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/fakeFoobankEmployeeKey.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getFreshFakeFooBankEmployeeCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("test_vectors/freshly_generated/fakeFoobankEmployeeCert.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getCrossSignedBarBankCaCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("cross_signed/barbankCaCert.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getCrossSignedBarBankEmployeeCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("cross_signed/barbankEmployeeCert.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getCrossSignedFooBankAdminCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("cross_signed/foobankAdminCert.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getCrossSignedFooBankCaCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("cross_signed/foobankCaCert.asc"));
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing getCrossSignedFooBankEmployeeCert() throws IOException {
|
||||
return PGPainless.readKeyRing().publicKeyRing(getTestResourceInputStream("cross_signed/foobankEmployeeCert.asc"));
|
||||
}
|
||||
|
||||
// Generate cross signed test vectors from freshly generated
|
||||
public void crossSign() throws IOException, PGPException {
|
||||
PGPSecretKeyRing freshFooBankCaKey = getFreshFooBankCaKey();
|
||||
PGPPublicKeyRing freshFooBankCaCert = getFreshFooBankCaCert();
|
||||
|
||||
PGPSecretKeyRing freshFooBankEmployeeKey = getFreshFooBankEmployeeKey();
|
||||
PGPPublicKeyRing freshFooBankEmployeeCert = getFreshFooBankEmployeeCert();
|
||||
|
||||
PGPSecretKeyRing freshFooBankAdminKey = getFreshFooBankAdminKey();
|
||||
PGPPublicKeyRing freshFooBankAdminCert = getFreshFooBankAdminCert();
|
||||
|
||||
PGPSecretKeyRing freshFooBankCustomerKey = getFreshFooBankCustomerKey();
|
||||
PGPPublicKeyRing freshFooBankCustomerCert = getFreshFooBankCustomerCert();
|
||||
|
||||
PGPSecretKeyRing freshBarBankCaKey = getFreshBarBankCaKey();
|
||||
PGPPublicKeyRing freshBarBankCaCert = getFreshBarBankCaCert();
|
||||
|
||||
PGPSecretKeyRing freshBarBankEmployeeKey = getFreshBarBankEmployeeKey();
|
||||
PGPPublicKeyRing freshBarBankEmployeeCert = getFreshBarBankEmployeeCert();
|
||||
|
||||
PGPSecretKeyRing freshFakeFooBankEmployeeKey = getFreshFakeFooBankEmployeeKey();
|
||||
PGPPublicKeyRing freshFakeFooBankEmployeeCert = getFreshFakeFooBankEmployeeCert();
|
||||
|
||||
final String fooBankRegex = "<[^>]+[@.]foobank\\.com>$";
|
||||
final String barBankRegex = "<[^>]+[@.]barbank\\.com>$";
|
||||
|
||||
// Foo CA signs Foo Employee
|
||||
PGPPublicKeyRing caCertifiedFooBankEmployeeCert = PGPainless.certify()
|
||||
.userIdOnCertificate("Foo Bank Employee <employee@foobank.com>", freshFooBankEmployeeCert)
|
||||
.withKey(freshFooBankCaKey, getFooBankCaProtector())
|
||||
.buildWithSubpackets(new CertificationSubpackets.Callback() {
|
||||
@Override
|
||||
public void modifyHashedSubpackets(CertificationSubpackets hashedSubpackets) {
|
||||
hashedSubpackets.addNotationData(false, "affiliation@foobank.com", "employee");
|
||||
}
|
||||
})
|
||||
.getCertifiedCertificate();
|
||||
|
||||
// Foo CA signs Foo Admin
|
||||
PGPPublicKeyRing caCertifiedFooBankAdminCert = PGPainless.certify()
|
||||
.userIdOnCertificate("Foo Bank Admin <admin@foobank.com>", freshFooBankAdminCert)
|
||||
.withKey(freshFooBankCaKey, getFooBankCaProtector())
|
||||
.buildWithSubpackets(new CertificationSubpackets.Callback() {
|
||||
@Override
|
||||
public void modifyHashedSubpackets(CertificationSubpackets hashedSubpackets) {
|
||||
hashedSubpackets.addNotationData(false, "affiliation@foobank.com", "administrator");
|
||||
}
|
||||
})
|
||||
.getCertifiedCertificate();
|
||||
|
||||
// Foo Employee delegates trust to Foo CA
|
||||
PGPPublicKeyRing employeeDelegatedCaCert = PGPainless.certify()
|
||||
.certificate(freshFooBankCaCert, Trustworthiness.fullyTrusted().introducer())
|
||||
.withKey(freshFooBankEmployeeKey, getFooBankEmployeeProtector())
|
||||
.buildWithSubpackets(new CertificationSubpackets.Callback() {
|
||||
@Override
|
||||
public void modifyHashedSubpackets(CertificationSubpackets hashedSubpackets) {
|
||||
hashedSubpackets.setRegularExpression(fooBankRegex);
|
||||
}
|
||||
})
|
||||
.getCertifiedCertificate();
|
||||
|
||||
// Foo Admin delegates trust to Foo CA
|
||||
PGPPublicKeyRing adminDelegatedCaCert = PGPainless.certify()
|
||||
.certificate(freshFooBankCaCert, Trustworthiness.fullyTrusted().introducer())
|
||||
.withKey(freshFooBankAdminKey, getFooBankAdminProtector())
|
||||
.buildWithSubpackets(new CertificationSubpackets.Callback() {
|
||||
@Override
|
||||
public void modifyHashedSubpackets(CertificationSubpackets hashedSubpackets) {
|
||||
hashedSubpackets.setRegularExpression(fooBankRegex);
|
||||
}
|
||||
})
|
||||
.getCertifiedCertificate();
|
||||
|
||||
// Customer delegates trust to Foo CA
|
||||
PGPPublicKeyRing customerDelegatedCaCert = PGPainless.certify()
|
||||
.certificate(freshFooBankCaCert, Trustworthiness.fullyTrusted().introducer())
|
||||
.withKey(freshFooBankCustomerKey, SecretKeyRingProtector.unprotectedKeys())
|
||||
.buildWithSubpackets(new CertificationSubpackets.Callback() {
|
||||
@Override
|
||||
public void modifyHashedSubpackets(CertificationSubpackets hashedSubpackets) {
|
||||
hashedSubpackets.setRegularExpression(fooBankRegex);
|
||||
}
|
||||
})
|
||||
.getCertifiedCertificate();
|
||||
|
||||
PGPPublicKeyRing mergedFooCa = PGPPublicKeyRing.join(employeeDelegatedCaCert, adminDelegatedCaCert);
|
||||
mergedFooCa = PGPPublicKeyRing.join(mergedFooCa, customerDelegatedCaCert);
|
||||
|
||||
// Foo Admin delegates trust to Bar CA
|
||||
PGPPublicKeyRing fooAdminDelegatedBarCa = PGPainless.certify()
|
||||
.certificate(freshBarBankCaCert, Trustworthiness.fullyTrusted().introducer())
|
||||
.withKey(freshFooBankAdminKey, getFooBankAdminProtector())
|
||||
.buildWithSubpackets(new CertificationSubpackets.Callback() {
|
||||
@Override
|
||||
public void modifyHashedSubpackets(CertificationSubpackets hashedSubpackets) {
|
||||
hashedSubpackets.setRegularExpression("<[^>]+[@.]barbank\\.com>$");
|
||||
}
|
||||
}).getCertifiedCertificate();
|
||||
|
||||
// Bar Employee delegates Bar CA
|
||||
PGPPublicKeyRing barEmployeeDelegatesBarCa = PGPainless.certify()
|
||||
.certificate(freshBarBankCaCert, Trustworthiness.fullyTrusted().introducer())
|
||||
.withKey(freshBarBankEmployeeKey, SecretKeyRingProtector.unprotectedKeys())
|
||||
.buildWithSubpackets(new CertificationSubpackets.Callback() {
|
||||
@Override
|
||||
public void modifyHashedSubpackets(CertificationSubpackets hashedSubpackets) {
|
||||
hashedSubpackets.setRegularExpression(barBankRegex);
|
||||
}
|
||||
})
|
||||
.getCertifiedCertificate();
|
||||
|
||||
PGPPublicKeyRing mergedBarCa = PGPPublicKeyRing.join(fooAdminDelegatedBarCa, barEmployeeDelegatesBarCa);
|
||||
|
||||
// Bar CA signs Bar Employee
|
||||
PGPPublicKeyRing barCaCertifiedEmployeeCert = PGPainless.certify()
|
||||
.userIdOnCertificate("Bar Bank Employee <employee@barbank.com>", freshBarBankEmployeeCert)
|
||||
.withKey(freshBarBankCaKey, SecretKeyRingProtector.unprotectedKeys())
|
||||
.buildWithSubpackets(new CertificationSubpackets.Callback() {
|
||||
@Override
|
||||
public void modifyHashedSubpackets(CertificationSubpackets hashedSubpackets) {
|
||||
hashedSubpackets.addNotationData(false, "affiliation@barbank.com", "employee");
|
||||
}
|
||||
})
|
||||
.getCertifiedCertificate();
|
||||
|
||||
System.out.println("Foo Employee");
|
||||
System.out.println(PGPainless.asciiArmor(caCertifiedFooBankEmployeeCert));
|
||||
|
||||
System.out.println("Foo Admin");
|
||||
System.out.println(PGPainless.asciiArmor(caCertifiedFooBankAdminCert));
|
||||
|
||||
System.out.println("Foo CA");
|
||||
System.out.println(PGPainless.asciiArmor(mergedFooCa));
|
||||
|
||||
System.out.println("Bar CA");
|
||||
System.out.println(PGPainless.asciiArmor(mergedBarCa));
|
||||
|
||||
System.out.println("Bar Employee");
|
||||
System.out.println(PGPainless.asciiArmor(barCaCertifiedEmployeeCert));
|
||||
}
|
||||
|
||||
private static InputStream getTestResourceInputStream(String resource) {
|
||||
InputStream inputStream = WotTestVectors.class.getClassLoader().getResourceAsStream(resource);
|
||||
if (inputStream == null) {
|
||||
throw new IllegalArgumentException(String.format("Unknown resource %s", resource));
|
||||
}
|
||||
return inputStream;
|
||||
}
|
||||
}
|
47
wot-dijkstra/src/test/resources/test_vectors/README.md
Normal file
47
wot-dijkstra/src/test/resources/test_vectors/README.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
# Test Vectors
|
||||
|
||||
## Freshly Generated Vectors
|
||||
|
||||
The `freshly_generated/` directory contains freshly generated test vectors.
|
||||
Those are keys and certificates without any third-party signatures.
|
||||
|
||||
```mermaid
|
||||
graph LR;
|
||||
a[Foo Bank CA <ca@foobank.com>];
|
||||
b[Foo Bank Employee <employee@foobank.com>];
|
||||
c[Foo Bank Admin <admin@foobank.com>];
|
||||
|
||||
d[Customer <customer@example.com>];
|
||||
e[Bar Bank CA <ca@barbank.com>];
|
||||
f[Bar Bank Employee <employee@barbank.com>];
|
||||
g[Foo Bank Employee (Attacker) <employee@foobank.com>];
|
||||
```
|
||||
|
||||
## Cross Signed Vectors
|
||||
The `cross_signed/` directory contains test vectors that model the following interconnectivity:
|
||||
|
||||
```mermaid
|
||||
graph TD;
|
||||
a[Foo Bank CA <ca@foobank.com>];
|
||||
b[Foo Bank Employee <employee@foobank.com>];
|
||||
c[Foo Bank Admin <admin@foobank.com>];
|
||||
|
||||
d[Customer <customer@example.com>];
|
||||
e[Bar Bank CA <ca@barbank.com>];
|
||||
f[Bar Bank Employee <employee@barbank.com>];
|
||||
g[Foo Bank Employee (Attacker) <employee@foobank.com>];
|
||||
|
||||
a -- generic certification --> b & c;
|
||||
b & c & d == 1:120:"<[^>]+[@.]foobank\.com>$" ==> a;
|
||||
e -- generic certification --> f;
|
||||
c == 1:120:"<[^>]+[@.]barbank\.com>$" ==> e;
|
||||
```
|
||||
|
||||
## Useful Resources:
|
||||
* https://mateam.net/html-escape-characters/
|
||||
* https://docs.sequoia-pgp.org/sequoia_openpgp/regex/index.html#caveat-emptor
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: CEE5 FB5D C524 84A6 9B73 9FAD 7DA0 2993 D86B 0355
|
||||
Comment: Bar Bank CA <ca@barbank.com>
|
||||
|
||||
mDMEYs1BgxYJKwYBBAHaRw8BAQdA3hOlkHAVyvTwePiifKnwLhkcJnswNidJ7dWo
|
||||
UKEQlZWIlAQfFgoARgUCYs1MVwkQ9fHjtTaNbKcWIQSmiOyyabOBYdpNc4/18eO1
|
||||
No1spxoGPFtePl0rW0AuXWJhcmJhbmtcLmNvbT4kAAOFAXgAAOfiAQCJIhMid48+
|
||||
8FS9IO76ELTYMsy0q/kBd4z3Gs+OYqM3ZQEAjyqoEKT8OHNC3zPkneE4bhsB21CH
|
||||
PMBe+pYcUE3tZAmIlAQfFgoARgUCYs1MVwkQitIzV+t4uwgWIQREeoKqA/fXpLHa
|
||||
TXyK0jNX63i7CBoGPFtePl0rW0AuXWJhcmJhbmtcLmNvbT4kAAOFAXgAAHb5AP99
|
||||
3BHUJhkpc6S0NiTsT1OKifmAuI9bRT++TIqEB6p7uwEA09k6Y59pjZhnISG4+KiK
|
||||
Y+ZhGKx6bbyGIXirwlssMQK0HEJhciBCYW5rIENBIDxjYUBiYXJiYW5rLmNvbT6I
|
||||
jwQTFgoAQQUCYs1BgwkQfaApk9hrA1UWIQTO5ftdxSSEpptzn619oCmT2GsDVQKe
|
||||
AQKbAQUWAgMBAAQLCQgHBRUKCQgLApkBAABltgEA4Ob86bhjLa6Xg4wnzYIGH21W
|
||||
EUlc37BBaqwr4qvfsA4A/0BzOdTy3Wty5DkdMiA2yPOxIVuHbTD8pgqpne2yoegN
|
||||
uDgEYs1BgxIKKwYBBAGXVQEFAQEHQDVHLNRE4Gtg9/BzwSUMW3psBD7OUm4Df0gg
|
||||
ilvLZhMcAwEIB4h1BBgWCgAdBQJizUGDAp4BApsMBRYCAwEABAsJCAcFFQoJCAsA
|
||||
CgkQfaApk9hrA1WrYAEAsZsEoUfAPIG6d9gtK7Zmm3zt+xiTELW08E/pwjmFTmEB
|
||||
AODICUhsEelkdXrbIkL2oWAKoxlsbkyA6mNXBt5UXQcJuDMEYs1BgxYJKwYBBAHa
|
||||
Rw8BAQdARHF/sgSTB38osF0+XA9N2htsMIrb322j3LQdrk0M77SI1QQYFgoAfQUC
|
||||
Ys1BgwKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmLNQYMACgkQtEK9
|
||||
w/vjcduqJwD+KW23+689GXtu4k3PW60GPsiue22lMGwZY7hsWvZB1hkBAPER8PMs
|
||||
0MJAQzcs2u6bYPMDI921umqX8Yvsob1kzs0FAAoJEH2gKZPYawNVdKwBAKz5zfwE
|
||||
KZ2dGPCsUJzAXfPxe0LmATcvS5g/LvAreD0KAQDfpfMFO2EqQu6wDt+e5MH3YCjG
|
||||
saYwvjB1elj1zhblAg==
|
||||
=O9uk
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,25 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: 447A 82AA 03F7 D7A4 B1DA 4D7C 8AD2 3357 EB78 BB08
|
||||
Comment: Bar Bank Employee <employee@barbank.com>
|
||||
|
||||
mDMEYs1CXRYJKwYBBAHaRw8BAQdAeMjyfqInb8aZppFsKHdndoeL3j08Z5CtagKz
|
||||
KPb6YGO0KEJhciBCYW5rIEVtcGxveWVlIDxlbXBsb3llZUBiYXJiYW5rLmNvbT6I
|
||||
jwQTFgoAQQUCYs1CXQkQitIzV+t4uwgWIQREeoKqA/fXpLHaTXyK0jNX63i7CAKe
|
||||
AQKbAQUWAgMBAAQLCQgHBRUKCQgLApkBAACgcgD7Bk2Hu+Lu3aA+IyzFyTQOw8zQ
|
||||
RUeA/X8neEIezYDTC+MA/jGFyiKW7b+6W3zgdIgiHMeWhG3Wx2oOC1izW2+tP2cJ
|
||||
iJ4EEBYKAFAFAmLNTFcJEH2gKZPYawNVFiEEzuX7XcUkhKabc5+tfaApk9hrA1Uo
|
||||
FIAAAAAAFwAIYWZmaWxpYXRpb25AYmFyYmFuay5jb21lbXBsb3llZQAAG4ABAMF2
|
||||
yTTckIjHNIyv7M8mcUx/zW++oJMxnhztT73cQeNxAQDVoyHqRgLpWR4lKw2b5IwE
|
||||
gw9KT9zIA9jveWufwgIvD7g4BGLNQl0SCisGAQQBl1UBBQEBB0Bq216nP4pw9r1F
|
||||
8OMcUWisYUvOePUeULPLYV+jX8UdBgMBCAeIdQQYFgoAHQUCYs1CXQKeAQKbDAUW
|
||||
AgMBAAQLCQgHBRUKCQgLAAoJEIrSM1freLsIo5QA/A9zDz8XrjtbJIK60J3kmwvY
|
||||
jynF9IkzyFTj4sNeHhJTAP92dTWvbogEwzPhBbofwzOT7PXg7laQ8v9eTqr50lWf
|
||||
B7gzBGLNQl0WCSsGAQQB2kcPAQEHQEV9SpoogW2HrVIaU953W31ceDvDmBWMFBQQ
|
||||
a3Qsz6IBiNUEGBYKAH0FAmLNQl0CngECmwIFFgIDAQAECwkIBwUVCgkIC18gBBkW
|
||||
CgAGBQJizUJdAAoJENG3C+ldSVbaPsAA/2giZHcGT0nyAojPds1IfIE97dyAlVgj
|
||||
fbTH3H4QNVsAAP9dpakDZya/q8M/wnZOyOBf+dHiUrvDbxiBo4tj2ANJBgAKCRCK
|
||||
0jNX63i7CMPgAP4ofxoiit/7nqvndk50NkiM9Ab0yTcLnjY+b28YMDLacwEApoEl
|
||||
I15NDk/uTXQRKtXrHKoORwSK3XRPSSGYgMOhkwQ=
|
||||
=LiUn
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,25 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: A688 ECB2 69B3 8161 DA4D 738F F5F1 E3B5 368D 6CA7
|
||||
Comment: Foo Bank Admin <admin@foobank.com>
|
||||
|
||||
mDMEYrcZeRYJKwYBBAHaRw8BAQdABkxvWXwHDD6B3hXYQehQkil3e3Gcg5mNbOdS
|
||||
9DSeny20IkZvbyBCYW5rIEFkbWluIDxhZG1pbkBmb29iYW5rLmNvbT6IjwQTFgoA
|
||||
QQUCYrcZeQkQ9fHjtTaNbKcWIQSmiOyyabOBYdpNc4/18eO1No1spwKeAQKbAQUW
|
||||
AgMBAAQLCQgHBRUKCQgLApkBAABPGwEAv0TWDMkJV6QiIgm8V4b9uIEzMpQku4UM
|
||||
FBHvMKXh0W8A/1pocJBG+K9srn8NJlPBul7duoNbkzzHANkytFVEBOgEiKMEEBYK
|
||||
AFUFAmLNTFYJEKFJBrdXMI7wFiEEMUVo/QiegSvLRR4roUkGt1cwjvAtFIAAAAAA
|
||||
FwANYWZmaWxpYXRpb25AZm9vYmFuay5jb21hZG1pbmlzdHJhdG9yAADddwEAkcJU
|
||||
aQfRbAkUvOp1zPUddQq6+a5C1y33d5s2n9gLqucBALy/kkbN5moNxEtcqTD5K1uD
|
||||
OFfEVEIOEAGE7ic1dWcMuDgEYrcZeRIKKwYBBAGXVQEFAQEHQDd1fqNPF27j+Z8B
|
||||
PtbyoU6sp63UQfBhJrUKNFjpWNZrAwEIB4h1BBgWCgAdBQJitxl5Ap4BApsMBRYC
|
||||
AwEABAsJCAcFFQoJCAsACgkQ9fHjtTaNbKdmNwEAzTQAJ/ML5puyGHR9zCbuAonD
|
||||
B0lKoXlJeWX7YpsLSugA/3M4YgzpBfoRNbehCbc2hcd/hbW9gKQs2k2iCUYDoPIN
|
||||
uDMEYrcZeRYJKwYBBAHaRw8BAQdAIF2e85PlS4PWquE3NLE59A1tr9KPxEznQ0Ej
|
||||
G/DfefiI1QQYFgoAfQUCYrcZeQKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYK
|
||||
AAYFAmK3GXkACgkQUqp0e41/iO+PswD+I+dgdOrj/jflr3D0Th8cugj9zFzwFYn9
|
||||
aaGmiee5wv8A/RvvJoiwHMTi3QPvR4zGdF5pibOccbWr/dggFjKXTmoFAAoJEPXx
|
||||
47U2jWynVbEBANEjxzV8KnujGor79Mc1ag9wxyDgihXRowsoKEnSkyHQAQD6n0Pf
|
||||
1q8fl7BxbreVeY+0HN2swKlO9TvO303a3X6/BQ==
|
||||
=WtDJ
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,31 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: 3145 68FD 089E 812B CB45 1E2B A149 06B7 5730 8EF0
|
||||
Comment: Foo Bank CA <ca@foobank.com>
|
||||
|
||||
mDMEYrcXbxYJKwYBBAHaRw8BAQdAPIIGHwbBzEIExd70jtw7uDvOymt0nU83uLqR
|
||||
4Og+cxCIlAQfFgoARgUCYs1MVgkQIa3PO58zSf4WIQQ7JP05RzLWqc9uBx4hrc87
|
||||
nzNJ/hoGPFtePl0rW0AuXWZvb2JhbmtcLmNvbT4kAAOFAXgAAD4CAPwIMuVG2f+b
|
||||
UfDsrtbQx8UO8TUnoRVHhwW/ogsp9Mop6AD/SoMIK1aNxzha56NOgCmAHo3hvdVp
|
||||
gflXu5SlMpDcBgyIlAQfFgoARgUCYs1MVwkQ9fHjtTaNbKcWIQSmiOyyabOBYdpN
|
||||
c4/18eO1No1spxoGPFtePl0rW0AuXWZvb2JhbmtcLmNvbT4kAAOFAXgAADS+AQD5
|
||||
L1q6IO6xnG7pz4iammO7yM5jseDKsnd06q1qXTOxRQEAsYz2dx0owhU+4qZwfEME
|
||||
kLIewdsBcjJurlXYDZJSkAuIlAQfFgoARgUCYs1MVwkQ+LtlEBaP0r8WIQQsTMpM
|
||||
iRd/0ZHuo+D4u2UQFo/SvxoGPFtePl0rW0AuXWZvb2JhbmtcLmNvbT4kAAOFAXgA
|
||||
AKlYAQDO0czTk2j+CRpYLwH24UT0NZQGcImx/xW+XHUz40y/mgEAmvkBkS0yaDET
|
||||
94Nq1FpCL0oaheEybrXPzVT5+67Q2QW0HEZvbyBCYW5rIENBIDxjYUBmb29iYW5r
|
||||
LmNvbT6IjwQTFgoAQQUCYrcXbwkQoUkGt1cwjvAWIQQxRWj9CJ6BK8tFHiuhSQa3
|
||||
VzCO8AKeAQKbAQUWAgMBAAQLCQgHBRUKCQgLApkBAAD+HwEAzElDjL9DRUnWd4yK
|
||||
jNoB+yT6RAQ9kD6BgRs/vKyQtqcBANfIysKFneE0B4lExhh6hB2cGoMR/YMhJHSd
|
||||
9zorxH4KuDgEYrcXbxIKKwYBBAGXVQEFAQEHQMmhBx59elwIbvvv3UOCLZn2VJDE
|
||||
gkOGq1DOpYRNXe8FAwEIB4h1BBgWCgAdBQJitxdvAp4BApsMBRYCAwEABAsJCAcF
|
||||
FQoJCAsACgkQoUkGt1cwjvDeBgEA9rsAaBuE5QWoZSH6FxHGpq5XY1rFpmvFKn7g
|
||||
hZ33p6ABAJZJx0RQZGDaI5f46Pjph2U7pvLm18mXNd5cYPQtcFoFuDMEYrcXcBYJ
|
||||
KwYBBAHaRw8BAQdAwdROT7LazWiJsEWF/bXjcAybl7TE3avv7GTVUT00MK2I1QQY
|
||||
FgoAfQUCYrcXcAKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmK3F3AA
|
||||
CgkQKvTwayMtR7nA+wEAzKlKeGvOSUtp5R07BTmOchkP0T3sZ0L4MvBBtpAuU5YA
|
||||
/jNm1162I/ceEEtKgyKp0wC5pqYR0b+AWgB0tPaMfaoJAAoJEKFJBrdXMI7w608A
|
||||
/0nV1NXH/U0pRzmNNeRJWAMlFXUdxhhYSlqrqYf2GQRlAP9NR9iMwpA0gFP+Uey3
|
||||
bCNC8MxtZLKhPkz2Rz4pnnfaDA==
|
||||
=y1eO
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,25 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: 3B24 FD39 4732 D6A9 CF6E 071E 21AD CF3B 9F33 49FE
|
||||
Comment: Foo Bank Employee <employee@foobank.com>
|
||||
|
||||
mDMEYrcZeRYJKwYBBAHaRw8BAQdAl9j10kv/7NRAZe+yATf7kkXOoHwkxaKkHzM3
|
||||
wMihWlu0KEZvbyBCYW5rIEVtcGxveWVlIDxlbXBsb3llZUBmb29iYW5rLmNvbT6I
|
||||
jwQTFgoAQQUCYrcZeQkQIa3PO58zSf4WIQQ7JP05RzLWqc9uBx4hrc87nzNJ/gKe
|
||||
AQKbAQUWAgMBAAQLCQgHBRUKCQgLApkBAAA1aAEAthiiVUcYiKVyzfzbrZLkQlTW
|
||||
NR6fu85fR5rPns2NUdwA/2vkrneDmkE04osRwUGLMh30iQzduQ3aFW0hEe2OmDUO
|
||||
iJ4EEBYKAFAFAmLNTFYJEKFJBrdXMI7wFiEEMUVo/QiegSvLRR4roUkGt1cwjvAo
|
||||
FIAAAAAAFwAIYWZmaWxpYXRpb25AZm9vYmFuay5jb21lbXBsb3llZQAAVjoBAOqF
|
||||
r5tscY9sx4sxEjgDJ+v4+Jd4KxU8bNljzde4BG+AAP9Dj2IhrpNe8CJJlncV2TC1
|
||||
55IXAr/DLYcKwzqEJrVxBLg4BGK3GXkSCisGAQQBl1UBBQEBB0ArMHy+cSuGxSdZ
|
||||
ScGncoxzB9ZLioxTRlR5Mqd/M2rNGAMBCAeIdQQYFgoAHQUCYrcZeQKeAQKbDAUW
|
||||
AgMBAAQLCQgHBRUKCQgLAAoJECGtzzufM0n+vDoA/A3p3LMYSUcvELY9Af3nC6/4
|
||||
71r5B3MDnURShddOtcRyAP96J1i5ExKnC+Ltw36/QtyxfyHp9Pr9Fw6BX/zq4EFo
|
||||
CrgzBGK3GXkWCSsGAQQB2kcPAQEHQPlVJ9tosjkDY81zIKll52Gh00rhhXY1jFEx
|
||||
x248kkrjiNUEGBYKAH0FAmK3GXkCngECmwIFFgIDAQAECwkIBwUVCgkIC18gBBkW
|
||||
CgAGBQJitxl5AAoJEJxjnlk3tCvcxesBAIf5xQYgRFfDeQlr63uglNCsvOXMa8BN
|
||||
LzgObT2nn8LrAQC+sqoWycLRFdFqg29E4OjhZd3iRvFWDoIfs67d9ywBDgAKCRAh
|
||||
rc87nzNJ/jWHAP4zs2k1MtV2eOcjUzdt8cfMsiHcD29uJsv6O628gqVdqgD/dvb5
|
||||
LPKxFPnfhSGOip0q2USrbBZLgupBjsNuVVu2IgM=
|
||||
=DqW1
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: CEE5 FB5D C524 84A6 9B73 9FAD 7DA0 2993 D86B 0355
|
||||
Comment: Bar Bank CA <ca@barbank.com>
|
||||
|
||||
mDMEYs1BgxYJKwYBBAHaRw8BAQdA3hOlkHAVyvTwePiifKnwLhkcJnswNidJ7dWo
|
||||
UKEQlZW0HEJhciBCYW5rIENBIDxjYUBiYXJiYW5rLmNvbT6IjwQTFgoAQQUCYs1B
|
||||
gwkQfaApk9hrA1UWIQTO5ftdxSSEpptzn619oCmT2GsDVQKeAQKbAQUWAgMBAAQL
|
||||
CQgHBRUKCQgLApkBAABltgEA4Ob86bhjLa6Xg4wnzYIGH21WEUlc37BBaqwr4qvf
|
||||
sA4A/0BzOdTy3Wty5DkdMiA2yPOxIVuHbTD8pgqpne2yoegNuDgEYs1BgxIKKwYB
|
||||
BAGXVQEFAQEHQDVHLNRE4Gtg9/BzwSUMW3psBD7OUm4Df0ggilvLZhMcAwEIB4h1
|
||||
BBgWCgAdBQJizUGDAp4BApsMBRYCAwEABAsJCAcFFQoJCAsACgkQfaApk9hrA1Wr
|
||||
YAEAsZsEoUfAPIG6d9gtK7Zmm3zt+xiTELW08E/pwjmFTmEBAODICUhsEelkdXrb
|
||||
IkL2oWAKoxlsbkyA6mNXBt5UXQcJuDMEYs1BgxYJKwYBBAHaRw8BAQdARHF/sgST
|
||||
B38osF0+XA9N2htsMIrb322j3LQdrk0M77SI1QQYFgoAfQUCYs1BgwKeAQKbAgUW
|
||||
AgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmLNQYMACgkQtEK9w/vjcduqJwD+KW23
|
||||
+689GXtu4k3PW60GPsiue22lMGwZY7hsWvZB1hkBAPER8PMs0MJAQzcs2u6bYPMD
|
||||
I921umqX8Yvsob1kzs0FAAoJEH2gKZPYawNVdKwBAKz5zfwEKZ2dGPCsUJzAXfPx
|
||||
e0LmATcvS5g/LvAreD0KAQDfpfMFO2EqQu6wDt+e5MH3YCjGsaYwvjB1elj1zhbl
|
||||
Ag==
|
||||
=Xlkn
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,24 @@
|
|||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: CEE5 FB5D C524 84A6 9B73 9FAD 7DA0 2993 D86B 0355
|
||||
Comment: Bar Bank CA <ca@barbank.com>
|
||||
|
||||
lFgEYs1BgxYJKwYBBAHaRw8BAQdA3hOlkHAVyvTwePiifKnwLhkcJnswNidJ7dWo
|
||||
UKEQlZUAAQD94AUX6NT0z3kvWb1CXxgZacF3w/r2IWV0lSc6WZDcQRBTtBxCYXIg
|
||||
QmFuayBDQSA8Y2FAYmFyYmFuay5jb20+iI8EExYKAEEFAmLNQYMJEH2gKZPYawNV
|
||||
FiEEzuX7XcUkhKabc5+tfaApk9hrA1UCngECmwEFFgIDAQAECwkIBwUVCgkICwKZ
|
||||
AQAAZbYBAODm/Om4Yy2ul4OMJ82CBh9tVhFJXN+wQWqsK+Kr37AOAP9AcznU8t1r
|
||||
cuQ5HTIgNsjzsSFbh20w/KYKqZ3tsqHoDZxdBGLNQYMSCisGAQQBl1UBBQEBB0A1
|
||||
RyzUROBrYPfwc8ElDFt6bAQ+zlJuA39IIIpby2YTHAMBCAcAAP9FU6uepd97vJ4y
|
||||
VTcvtzPXG9jvpgNw50pDfV90z6VAwBEaiHUEGBYKAB0FAmLNQYMCngECmwwFFgID
|
||||
AQAECwkIBwUVCgkICwAKCRB9oCmT2GsDVatgAQCxmwShR8A8gbp32C0rtmabfO37
|
||||
GJMQtbTwT+nCOYVOYQEA4MgJSGwR6WR1etsiQvahYAqjGWxuTIDqY1cG3lRdBwmc
|
||||
WARizUGDFgkrBgEEAdpHDwEBB0BEcX+yBJMHfyiwXT5cD03aG2wwitvfbaPctB2u
|
||||
TQzvtAABAJJiLD7fXGdr/liZ//EOhJ4YicrcxXahud6q3OYA1QUJEYSI1QQYFgoA
|
||||
fQUCYs1BgwKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmLNQYMACgkQ
|
||||
tEK9w/vjcduqJwD+KW23+689GXtu4k3PW60GPsiue22lMGwZY7hsWvZB1hkBAPER
|
||||
8PMs0MJAQzcs2u6bYPMDI921umqX8Yvsob1kzs0FAAoJEH2gKZPYawNVdKwBAKz5
|
||||
zfwEKZ2dGPCsUJzAXfPxe0LmATcvS5g/LvAreD0KAQDfpfMFO2EqQu6wDt+e5MH3
|
||||
YCjGsaYwvjB1elj1zhblAg==
|
||||
=owuy
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: 447A 82AA 03F7 D7A4 B1DA 4D7C 8AD2 3357 EB78 BB08
|
||||
Comment: Bar Bank Employee <employee@barbank.com>
|
||||
|
||||
mDMEYs1CXRYJKwYBBAHaRw8BAQdAeMjyfqInb8aZppFsKHdndoeL3j08Z5CtagKz
|
||||
KPb6YGO0KEJhciBCYW5rIEVtcGxveWVlIDxlbXBsb3llZUBiYXJiYW5rLmNvbT6I
|
||||
jwQTFgoAQQUCYs1CXQkQitIzV+t4uwgWIQREeoKqA/fXpLHaTXyK0jNX63i7CAKe
|
||||
AQKbAQUWAgMBAAQLCQgHBRUKCQgLApkBAACgcgD7Bk2Hu+Lu3aA+IyzFyTQOw8zQ
|
||||
RUeA/X8neEIezYDTC+MA/jGFyiKW7b+6W3zgdIgiHMeWhG3Wx2oOC1izW2+tP2cJ
|
||||
uDgEYs1CXRIKKwYBBAGXVQEFAQEHQGrbXqc/inD2vUXw4xxRaKxhS8549R5Qs8th
|
||||
X6NfxR0GAwEIB4h1BBgWCgAdBQJizUJdAp4BApsMBRYCAwEABAsJCAcFFQoJCAsA
|
||||
CgkQitIzV+t4uwijlAD8D3MPPxeuO1skgrrQneSbC9iPKcX0iTPIVOPiw14eElMA
|
||||
/3Z1Na9uiATDM+EFuh/DM5Ps9eDuVpDy/15OqvnSVZ8HuDMEYs1CXRYJKwYBBAHa
|
||||
Rw8BAQdARX1KmiiBbYetUhpT3ndbfVx4O8OYFYwUFBBrdCzPogGI1QQYFgoAfQUC
|
||||
Ys1CXQKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmLNQl0ACgkQ0bcL
|
||||
6V1JVto+wAD/aCJkdwZPSfICiM92zUh8gT3t3ICVWCN9tMfcfhA1WwAA/12lqQNn
|
||||
Jr+rwz/Cdk7I4F/50eJSu8NvGIGji2PYA0kGAAoJEIrSM1freLsIw+AA/ih/GiKK
|
||||
3/ueq+d2TnQ2SIz0BvTJNwueNj5vbxgwMtpzAQCmgSUjXk0OT+5NdBEq1escqg5H
|
||||
BIrddE9JIZiAw6GTBA==
|
||||
=F0kp
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,24 @@
|
|||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: 447A 82AA 03F7 D7A4 B1DA 4D7C 8AD2 3357 EB78 BB08
|
||||
Comment: Bar Bank Employee <employee@barbank.com>
|
||||
|
||||
lFgEYs1CXRYJKwYBBAHaRw8BAQdAeMjyfqInb8aZppFsKHdndoeL3j08Z5CtagKz
|
||||
KPb6YGMAAP0WnOME3m2zmC6ujA1r5s1RLCtIsrWnkX62ICDCJzIWLA8ftChCYXIg
|
||||
QmFuayBFbXBsb3llZSA8ZW1wbG95ZWVAYmFyYmFuay5jb20+iI8EExYKAEEFAmLN
|
||||
Ql0JEIrSM1freLsIFiEERHqCqgP316Sx2k18itIzV+t4uwgCngECmwEFFgIDAQAE
|
||||
CwkIBwUVCgkICwKZAQAAoHIA+wZNh7vi7t2gPiMsxck0DsPM0EVHgP1/J3hCHs2A
|
||||
0wvjAP4xhcoilu2/ult84HSIIhzHloRt1sdqDgtYs1tvrT9nCZxdBGLNQl0SCisG
|
||||
AQQBl1UBBQEBB0Bq216nP4pw9r1F8OMcUWisYUvOePUeULPLYV+jX8UdBgMBCAcA
|
||||
AP9AYnLs0zkVSsWMxZDMTCoW1fjrgjzOMGeZRtsvrcUA2BF2iHUEGBYKAB0FAmLN
|
||||
Ql0CngECmwwFFgIDAQAECwkIBwUVCgkICwAKCRCK0jNX63i7CKOUAPwPcw8/F647
|
||||
WySCutCd5JsL2I8pxfSJM8hU4+LDXh4SUwD/dnU1r26IBMMz4QW6H8Mzk+z14O5W
|
||||
kPL/Xk6q+dJVnwecWARizUJdFgkrBgEEAdpHDwEBB0BFfUqaKIFth61SGlPed1t9
|
||||
XHg7w5gVjBQUEGt0LM+iAQAA/3fH0f3QPCOEpByws3f4Ovqjc+IzwFmA6mmwlxN2
|
||||
9wNpEs6I1QQYFgoAfQUCYs1CXQKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYK
|
||||
AAYFAmLNQl0ACgkQ0bcL6V1JVto+wAD/aCJkdwZPSfICiM92zUh8gT3t3ICVWCN9
|
||||
tMfcfhA1WwAA/12lqQNnJr+rwz/Cdk7I4F/50eJSu8NvGIGji2PYA0kGAAoJEIrS
|
||||
M1freLsIw+AA/ih/GiKK3/ueq+d2TnQ2SIz0BvTJNwueNj5vbxgwMtpzAQCmgSUj
|
||||
Xk0OT+5NdBEq1escqg5HBIrddE9JIZiAw6GTBA==
|
||||
=7A42
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: E5C1 A41B E6BF 8E3D 595B 7214 2284 3AF5 6677 E9B0
|
||||
Comment: Foo Bank Employee (Attacker) <employee@foobank.com>
|
||||
|
||||
mDMEYs1CoBYJKwYBBAHaRw8BAQdAKjGuG4PPeUX1utitGGofxfcD5Sqf7c9CtyFA
|
||||
qXA/gd60M0ZvbyBCYW5rIEVtcGxveWVlIChBdHRhY2tlcikgPGVtcGxveWVlQGZv
|
||||
b2JhbmsuY29tPoiPBBMWCgBBBQJizUKgCRAihDr1ZnfpsBYhBOXBpBvmv449WVty
|
||||
FCKEOvVmd+mwAp4BApsBBRYCAwEABAsJCAcFFQoJCAsCmQEAAAorAP9E0cnmX26P
|
||||
zIcvmYXqCH4gfkH/18efgKAtLxFwz3yxuwD+KFj4xDxWy/KTmFRhJmFDBhJsNUiH
|
||||
tNEbVpBV1GiVLQ24OARizUKgEgorBgEEAZdVAQUBAQdAU05+Xcun+77TCwhuwW/Y
|
||||
zdKexCD+6u4EPUXyDKo0rjMDAQgHiHUEGBYKAB0FAmLNQqACngECmwwFFgIDAQAE
|
||||
CwkIBwUVCgkICwAKCRAihDr1ZnfpsHH/AQDsxQe/sYVqwgtSFbOxw/hmGMEmMOwx
|
||||
E5B+Pk7lLGcC3gEAuaFwRb4UgOeadQQ7eerxE0djYjL+wpqYLkYZrWI1dAy4MwRi
|
||||
zUKgFgkrBgEEAdpHDwEBB0Dc1odFo5f84ciLYjkWoH0jAMlZxc/TLMS1yi5lrohX
|
||||
M4jVBBgWCgB9BQJizUKgAp4BApsCBRYCAwEABAsJCAcFFQoJCAtfIAQZFgoABgUC
|
||||
Ys1CoAAKCRDcHi36XF9/A/pLAQD7tDyLsbP+s6jV4ZloujljLdxIlhg+g/87ruhc
|
||||
ISPRKQD/bTubvAzC8ixoYAWGPZOEut03b+zJ4g1l8SHXVCb+HwkACgkQIoQ69WZ3
|
||||
6bAdwgEAhZb7c0u07FlgxXkEIPIt45DwZbML37hxaVjF08NlwssBAJInWO4Gcj8T
|
||||
L0GLRaWEF+8dpPp+WtgklVkFel7f2XoK
|
||||
=UN4g
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,24 @@
|
|||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: E5C1 A41B E6BF 8E3D 595B 7214 2284 3AF5 6677 E9B0
|
||||
Comment: Foo Bank Employee (Attacker) <employee@foobank.com>
|
||||
|
||||
lFgEYs1CoBYJKwYBBAHaRw8BAQdAKjGuG4PPeUX1utitGGofxfcD5Sqf7c9CtyFA
|
||||
qXA/gd4AAP4vJXkVBVsASvS7pwIJXVuqKDb7yeCaR6Bjh9ihE1tZQw49tDNGb28g
|
||||
QmFuayBFbXBsb3llZSAoQXR0YWNrZXIpIDxlbXBsb3llZUBmb29iYW5rLmNvbT6I
|
||||
jwQTFgoAQQUCYs1CoAkQIoQ69WZ36bAWIQTlwaQb5r+OPVlbchQihDr1ZnfpsAKe
|
||||
AQKbAQUWAgMBAAQLCQgHBRUKCQgLApkBAAAKKwD/RNHJ5l9uj8yHL5mF6gh+IH5B
|
||||
/9fHn4CgLS8RcM98sbsA/ihY+MQ8Vsvyk5hUYSZhQwYSbDVIh7TRG1aQVdRolS0N
|
||||
nF0EYs1CoBIKKwYBBAGXVQEFAQEHQFNOfl3Lp/u+0wsIbsFv2M3SnsQg/uruBD1F
|
||||
8gyqNK4zAwEIBwAA/2grdpHBu9eI82YNrx+/ljb69FIYK2ttBVmL5awfh9rgEWiI
|
||||
dQQYFgoAHQUCYs1CoAKeAQKbDAUWAgMBAAQLCQgHBRUKCQgLAAoJECKEOvVmd+mw
|
||||
cf8BAOzFB7+xhWrCC1IVs7HD+GYYwSYw7DETkH4+TuUsZwLeAQC5oXBFvhSA55p1
|
||||
BDt56vETR2NiMv7CmpguRhmtYjV0DJxYBGLNQqAWCSsGAQQB2kcPAQEHQNzWh0Wj
|
||||
l/zhyItiORagfSMAyVnFz9MsxLXKLmWuiFczAAEA+xMd+XrMwFJisctVMfZaaPDR
|
||||
QZ7EAVeCZU2UAe0u1ssQ2ojVBBgWCgB9BQJizUKgAp4BApsCBRYCAwEABAsJCAcF
|
||||
FQoJCAtfIAQZFgoABgUCYs1CoAAKCRDcHi36XF9/A/pLAQD7tDyLsbP+s6jV4Zlo
|
||||
ujljLdxIlhg+g/87ruhcISPRKQD/bTubvAzC8ixoYAWGPZOEut03b+zJ4g1l8SHX
|
||||
VCb+HwkACgkQIoQ69WZ36bAdwgEAhZb7c0u07FlgxXkEIPIt45DwZbML37hxaVjF
|
||||
08NlwssBAJInWO4Gcj8TL0GLRaWEF+8dpPp+WtgklVkFel7f2XoK
|
||||
=saRS
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: A688 ECB2 69B3 8161 DA4D 738F F5F1 E3B5 368D 6CA7
|
||||
Comment: Foo Bank Admin <admin@foobank.com>
|
||||
|
||||
mDMEYrcZeRYJKwYBBAHaRw8BAQdABkxvWXwHDD6B3hXYQehQkil3e3Gcg5mNbOdS
|
||||
9DSeny20IkZvbyBCYW5rIEFkbWluIDxhZG1pbkBmb29iYW5rLmNvbT6IjwQTFgoA
|
||||
QQUCYrcZeQkQ9fHjtTaNbKcWIQSmiOyyabOBYdpNc4/18eO1No1spwKeAQKbAQUW
|
||||
AgMBAAQLCQgHBRUKCQgLApkBAABPGwEAv0TWDMkJV6QiIgm8V4b9uIEzMpQku4UM
|
||||
FBHvMKXh0W8A/1pocJBG+K9srn8NJlPBul7duoNbkzzHANkytFVEBOgEuDgEYrcZ
|
||||
eRIKKwYBBAGXVQEFAQEHQDd1fqNPF27j+Z8BPtbyoU6sp63UQfBhJrUKNFjpWNZr
|
||||
AwEIB4h1BBgWCgAdBQJitxl5Ap4BApsMBRYCAwEABAsJCAcFFQoJCAsACgkQ9fHj
|
||||
tTaNbKdmNwEAzTQAJ/ML5puyGHR9zCbuAonDB0lKoXlJeWX7YpsLSugA/3M4Ygzp
|
||||
BfoRNbehCbc2hcd/hbW9gKQs2k2iCUYDoPINuDMEYrcZeRYJKwYBBAHaRw8BAQdA
|
||||
IF2e85PlS4PWquE3NLE59A1tr9KPxEznQ0EjG/DfefiI1QQYFgoAfQUCYrcZeQKe
|
||||
AQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmK3GXkACgkQUqp0e41/iO+P
|
||||
swD+I+dgdOrj/jflr3D0Th8cugj9zFzwFYn9aaGmiee5wv8A/RvvJoiwHMTi3QPv
|
||||
R4zGdF5pibOccbWr/dggFjKXTmoFAAoJEPXx47U2jWynVbEBANEjxzV8KnujGor7
|
||||
9Mc1ag9wxyDgihXRowsoKEnSkyHQAQD6n0Pf1q8fl7BxbreVeY+0HN2swKlO9TvO
|
||||
303a3X6/BQ==
|
||||
=CESR
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: A688 ECB2 69B3 8161 DA4D 738F F5F1 E3B5 368D 6CA7
|
||||
Comment: Foo Bank Admin <admin@foobank.com>
|
||||
|
||||
lIYEYrcZeRYJKwYBBAHaRw8BAQdABkxvWXwHDD6B3hXYQehQkil3e3Gcg5mNbOdS
|
||||
9DSeny3+CQMCmWVHhM7xa9pgkX1Z8T7FcGWpMD91IgrF+7Bh97UtTFw8a71ixXww
|
||||
pH/86zqEE9mry/95KvxsiCSPYwKhS0j4S20mZfQriWvpM3gCZSbhMrQiRm9vIEJh
|
||||
bmsgQWRtaW4gPGFkbWluQGZvb2JhbmsuY29tPoiPBBMWCgBBBQJitxl5CRD18eO1
|
||||
No1spxYhBKaI7LJps4Fh2k1zj/Xx47U2jWynAp4BApsBBRYCAwEABAsJCAcFFQoJ
|
||||
CAsCmQEAAE8bAQC/RNYMyQlXpCIiCbxXhv24gTMylCS7hQwUEe8wpeHRbwD/Wmhw
|
||||
kEb4r2yufw0mU8G6Xt26g1uTPMcA2TK0VUQE6ASciwRitxl5EgorBgEEAZdVAQUB
|
||||
AQdAN3V+o08XbuP5nwE+1vKhTqynrdRB8GEmtQo0WOlY1msDAQgH/gkDApllR4TO
|
||||
8WvaYNfxPfMWsBQLze2hTDxhCds/kO7WSwJw0cN3IzGlCrv+KSId5n6Rfz4NtlWN
|
||||
yBOIecPZbzK4H0RSIc7jqiNRMqznpclwNYGIdQQYFgoAHQUCYrcZeQKeAQKbDAUW
|
||||
AgMBAAQLCQgHBRUKCQgLAAoJEPXx47U2jWynZjcBAM00ACfzC+abshh0fcwm7gKJ
|
||||
wwdJSqF5SXll+2KbC0roAP9zOGIM6QX6ETW3oQm3NoXHf4W1vYCkLNpNoglGA6Dy
|
||||
DZyGBGK3GXkWCSsGAQQB2kcPAQEHQCBdnvOT5UuD1qrhNzSxOfQNba/Sj8RM50NB
|
||||
Ixvw33n4/gkDApllR4TO8WvaYFyjXSJ54rtghVxVYEUd4OFWaGmKO5+KMEgqCqv4
|
||||
TWhfaqT+wV5hKJbx/IkgZFS9viFTK7BEEhaApslO/TuE6nFSoi7gVw+I1QQYFgoA
|
||||
fQUCYrcZeQKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmK3GXkACgkQ
|
||||
Uqp0e41/iO+PswD+I+dgdOrj/jflr3D0Th8cugj9zFzwFYn9aaGmiee5wv8A/Rvv
|
||||
JoiwHMTi3QPvR4zGdF5pibOccbWr/dggFjKXTmoFAAoJEPXx47U2jWynVbEBANEj
|
||||
xzV8KnujGor79Mc1ag9wxyDgihXRowsoKEnSkyHQAQD6n0Pf1q8fl7BxbreVeY+0
|
||||
HN2swKlO9TvO303a3X6/BQ==
|
||||
=4xhi
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: 3145 68FD 089E 812B CB45 1E2B A149 06B7 5730 8EF0
|
||||
Comment: Foo Bank CA <ca@foobank.com>
|
||||
Comment: Freshly generated cert
|
||||
|
||||
mDMEYrcXbxYJKwYBBAHaRw8BAQdAPIIGHwbBzEIExd70jtw7uDvOymt0nU83uLqR
|
||||
4Og+cxC0HEZvbyBCYW5rIENBIDxjYUBmb29iYW5rLmNvbT6IjwQTFgoAQQUCYrcX
|
||||
bwkQoUkGt1cwjvAWIQQxRWj9CJ6BK8tFHiuhSQa3VzCO8AKeAQKbAQUWAgMBAAQL
|
||||
CQgHBRUKCQgLApkBAAD+HwEAzElDjL9DRUnWd4yKjNoB+yT6RAQ9kD6BgRs/vKyQ
|
||||
tqcBANfIysKFneE0B4lExhh6hB2cGoMR/YMhJHSd9zorxH4KuDgEYrcXbxIKKwYB
|
||||
BAGXVQEFAQEHQMmhBx59elwIbvvv3UOCLZn2VJDEgkOGq1DOpYRNXe8FAwEIB4h1
|
||||
BBgWCgAdBQJitxdvAp4BApsMBRYCAwEABAsJCAcFFQoJCAsACgkQoUkGt1cwjvDe
|
||||
BgEA9rsAaBuE5QWoZSH6FxHGpq5XY1rFpmvFKn7ghZ33p6ABAJZJx0RQZGDaI5f4
|
||||
6Pjph2U7pvLm18mXNd5cYPQtcFoFuDMEYrcXcBYJKwYBBAHaRw8BAQdAwdROT7La
|
||||
zWiJsEWF/bXjcAybl7TE3avv7GTVUT00MK2I1QQYFgoAfQUCYrcXcAKeAQKbAgUW
|
||||
AgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmK3F3AACgkQKvTwayMtR7nA+wEAzKlK
|
||||
eGvOSUtp5R07BTmOchkP0T3sZ0L4MvBBtpAuU5YA/jNm1162I/ceEEtKgyKp0wC5
|
||||
pqYR0b+AWgB0tPaMfaoJAAoJEKFJBrdXMI7w608A/0nV1NXH/U0pRzmNNeRJWAMl
|
||||
FXUdxhhYSlqrqYf2GQRlAP9NR9iMwpA0gFP+Uey3bCNC8MxtZLKhPkz2Rz4pnnfa
|
||||
DA==
|
||||
=5bcR
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: 3145 68FD 089E 812B CB45 1E2B A149 06B7 5730 8EF0
|
||||
Comment: Foo Bank CA <ca@foobank.com>
|
||||
Comment: Freshly generated key
|
||||
|
||||
lIYEYrcXbxYJKwYBBAHaRw8BAQdAPIIGHwbBzEIExd70jtw7uDvOymt0nU83uLqR
|
||||
4Og+cxD+CQMCgCIAcSyeuXJgGMgKUCcU6tDwfGXgIVy0wCoDsSX2qWTKkyOYf7fu
|
||||
Kw0BmQ4bFhQtC9hpPDhCKuR0cLUV8f0TEU0/Vq4n5hNy2erDW60KFLQcRm9vIEJh
|
||||
bmsgQ0EgPGNhQGZvb2JhbmsuY29tPoiPBBMWCgBBBQJitxdvCRChSQa3VzCO8BYh
|
||||
BDFFaP0InoEry0UeK6FJBrdXMI7wAp4BApsBBRYCAwEABAsJCAcFFQoJCAsCmQEA
|
||||
AP4fAQDMSUOMv0NFSdZ3jIqM2gH7JPpEBD2QPoGBGz+8rJC2pwEA18jKwoWd4TQH
|
||||
iUTGGHqEHZwagxH9gyEkdJ33OivEfgqciwRitxdvEgorBgEEAZdVAQUBAQdAyaEH
|
||||
Hn16XAhu++/dQ4ItmfZUkMSCQ4arUM6lhE1d7wUDAQgH/gkDAoAiAHEsnrlyYPCE
|
||||
d+xi8TsvxtcWq7lO+wwMxg/et4nFijmCDwK5/1duRutt/UYj1PU1cicfJ9choM6l
|
||||
5qui74PBr5yTkUvNhAOjWfH60heIdQQYFgoAHQUCYrcXbwKeAQKbDAUWAgMBAAQL
|
||||
CQgHBRUKCQgLAAoJEKFJBrdXMI7w3gYBAPa7AGgbhOUFqGUh+hcRxqauV2NaxaZr
|
||||
xSp+4IWd96egAQCWScdEUGRg2iOX+Oj46YdlO6by5tfJlzXeXGD0LXBaBZyGBGK3
|
||||
F3AWCSsGAQQB2kcPAQEHQMHUTk+y2s1oibBFhf2143AMm5e0xN2r7+xk1VE9NDCt
|
||||
/gkDAoAiAHEsnrlyYFpx33LyuL4N/uccem+lZPCwjSum+Turs6vZQNY3toahx0fm
|
||||
i68BGm1Eu716NN/5AV3nXXa9jmf6PsesiEvfWWITo8A7/fuI1QQYFgoAfQUCYrcX
|
||||
cAKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmK3F3AACgkQKvTwayMt
|
||||
R7nA+wEAzKlKeGvOSUtp5R07BTmOchkP0T3sZ0L4MvBBtpAuU5YA/jNm1162I/ce
|
||||
EEtKgyKp0wC5pqYR0b+AWgB0tPaMfaoJAAoJEKFJBrdXMI7w608A/0nV1NXH/U0p
|
||||
RzmNNeRJWAMlFXUdxhhYSlqrqYf2GQRlAP9NR9iMwpA0gFP+Uey3bCNC8MxtZLKh
|
||||
Pkz2Rz4pnnfaDA==
|
||||
=roYK
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: 2C4C CA4C 8917 7FD1 91EE A3E0 F8BB 6510 168F D2BF
|
||||
Comment: Customer <customer@example.com>
|
||||
|
||||
mDMEYrcaYxYJKwYBBAHaRw8BAQdAIt6xhlPPq2wTnLO2CqMvoP2yk68NBSPJWkse
|
||||
85lCWee0H0N1c3RvbWVyIDxjdXN0b21lckBleGFtcGxlLmNvbT6IjwQTFgoAQQUC
|
||||
YrcaYwkQ+LtlEBaP0r8WIQQsTMpMiRd/0ZHuo+D4u2UQFo/SvwKeAQKbAQUWAgMB
|
||||
AAQLCQgHBRUKCQgLApkBAAB3agEAmnfM6RToSpLfBFXOERCAz0v9FYfskTz7bsGM
|
||||
CPSEgYcBAK85o67OTq5y8JhHHFfHvHeIIaE/IAzJLQHRNT3pvJ8KuDgEYrcaYxIK
|
||||
KwYBBAGXVQEFAQEHQGZVHqBfmrVSMbIFQUSE9bnj9JU/W/JUFuPHbd5csPpdAwEI
|
||||
B4h1BBgWCgAdBQJitxpjAp4BApsMBRYCAwEABAsJCAcFFQoJCAsACgkQ+LtlEBaP
|
||||
0r/XhAD+LHlbWcUil8HmnWpBSwcv3wBP1vnt4zPOlm1pkfmgq/MA/3hb+I0kQNMR
|
||||
7ux3bYORuLiHulapQtYQG5GGRfbvJqMPuDMEYrcaYxYJKwYBBAHaRw8BAQdAy3Jc
|
||||
mVMbgm6VT88y2xNCFrePTBXaroz90pI9mWBjvMGI1QQYFgoAfQUCYrcaYwKeAQKb
|
||||
AgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmK3GmMACgkQZTy2QngzKnRNbwEA
|
||||
pyuSMaZgI+kOaiFiIxCGXoR/zhYKKMyoq5JlF1W+cwQBALwz2ixIlP3jzT7wJnDf
|
||||
hcK7n/SMlXD4MIqR2CQkxRYHAAoJEPi7ZRAWj9K/Cb0A/24XLgqc5mj+MT0gyXRU
|
||||
GBOpoTlpZ86bpb4LcLycRU+0AQDuAq/i1n6e/noMRsbOh0WYZbqAbOgiv9DVJcRQ
|
||||
4kfpAg==
|
||||
=r4Bk
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,24 @@
|
|||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: 2C4C CA4C 8917 7FD1 91EE A3E0 F8BB 6510 168F D2BF
|
||||
Comment: Customer <customer@example.com>
|
||||
|
||||
lFgEYrcaYxYJKwYBBAHaRw8BAQdAIt6xhlPPq2wTnLO2CqMvoP2yk68NBSPJWkse
|
||||
85lCWecAAP9xVDxOuRZ5pDo5cSKIJf50dMNp/I/xHkNDHPNcI+ManQ9HtB9DdXN0
|
||||
b21lciA8Y3VzdG9tZXJAZXhhbXBsZS5jb20+iI8EExYKAEEFAmK3GmMJEPi7ZRAW
|
||||
j9K/FiEELEzKTIkXf9GR7qPg+LtlEBaP0r8CngECmwEFFgIDAQAECwkIBwUVCgkI
|
||||
CwKZAQAAd2oBAJp3zOkU6EqS3wRVzhEQgM9L/RWH7JE8+27BjAj0hIGHAQCvOaOu
|
||||
zk6ucvCYRxxXx7x3iCGhPyAMyS0B0TU96byfCpxdBGK3GmMSCisGAQQBl1UBBQEB
|
||||
B0BmVR6gX5q1UjGyBUFEhPW54/SVP1vyVBbjx23eXLD6XQMBCAcAAP9iOym80xxC
|
||||
El9TTF/k7mfRBjNocGBMMUP5nnkPwdag2A+KiHUEGBYKAB0FAmK3GmMCngECmwwF
|
||||
FgIDAQAECwkIBwUVCgkICwAKCRD4u2UQFo/Sv9eEAP4seVtZxSKXweadakFLBy/f
|
||||
AE/W+e3jM86WbWmR+aCr8wD/eFv4jSRA0xHu7Hdtg5G4uIe6VqlC1hAbkYZF9u8m
|
||||
ow+cWARitxpjFgkrBgEEAdpHDwEBB0DLclyZUxuCbpVPzzLbE0IWt49MFdqujP3S
|
||||
kj2ZYGO8wQABAPY7EMCZEq/sSDkldwv/43GUe4KuDay7Q6jHugO+YkkqD3iI1QQY
|
||||
FgoAfQUCYrcaYwKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmK3GmMA
|
||||
CgkQZTy2QngzKnRNbwEApyuSMaZgI+kOaiFiIxCGXoR/zhYKKMyoq5JlF1W+cwQB
|
||||
ALwz2ixIlP3jzT7wJnDfhcK7n/SMlXD4MIqR2CQkxRYHAAoJEPi7ZRAWj9K/Cb0A
|
||||
/24XLgqc5mj+MT0gyXRUGBOpoTlpZ86bpb4LcLycRU+0AQDuAq/i1n6e/noMRsbO
|
||||
h0WYZbqAbOgiv9DVJcRQ4kfpAg==
|
||||
=th8Q
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: 3B24 FD39 4732 D6A9 CF6E 071E 21AD CF3B 9F33 49FE
|
||||
Comment: Foo Bank Employee <employee@foobank.com>
|
||||
|
||||
mDMEYrcZeRYJKwYBBAHaRw8BAQdAl9j10kv/7NRAZe+yATf7kkXOoHwkxaKkHzM3
|
||||
wMihWlu0KEZvbyBCYW5rIEVtcGxveWVlIDxlbXBsb3llZUBmb29iYW5rLmNvbT6I
|
||||
jwQTFgoAQQUCYrcZeQkQIa3PO58zSf4WIQQ7JP05RzLWqc9uBx4hrc87nzNJ/gKe
|
||||
AQKbAQUWAgMBAAQLCQgHBRUKCQgLApkBAAA1aAEAthiiVUcYiKVyzfzbrZLkQlTW
|
||||
NR6fu85fR5rPns2NUdwA/2vkrneDmkE04osRwUGLMh30iQzduQ3aFW0hEe2OmDUO
|
||||
uDgEYrcZeRIKKwYBBAGXVQEFAQEHQCswfL5xK4bFJ1lJwadyjHMH1kuKjFNGVHky
|
||||
p38zas0YAwEIB4h1BBgWCgAdBQJitxl5Ap4BApsMBRYCAwEABAsJCAcFFQoJCAsA
|
||||
CgkQIa3PO58zSf68OgD8DencsxhJRy8Qtj0B/ecLr/jvWvkHcwOdRFKF1061xHIA
|
||||
/3onWLkTEqcL4u3Dfr9C3LF/Ien0+v0XDoFf/OrgQWgKuDMEYrcZeRYJKwYBBAHa
|
||||
Rw8BAQdA+VUn22iyOQNjzXMgqWXnYaHTSuGFdjWMUTHHbjySSuOI1QQYFgoAfQUC
|
||||
YrcZeQKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmK3GXkACgkQnGOe
|
||||
WTe0K9zF6wEAh/nFBiBEV8N5CWvre6CU0Ky85cxrwE0vOA5tPaefwusBAL6yqhbJ
|
||||
wtEV0WqDb0Tg6OFl3eJG8VYOgh+zrt33LAEOAAoJECGtzzufM0n+NYcA/jOzaTUy
|
||||
1XZ45yNTN23xx8yyIdwPb24my/o7rbyCpV2qAP929vks8rEU+d+FIY6KnSrZRKts
|
||||
FkuC6kGOw25VW7YiAw==
|
||||
=/hVO
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
Version: PGPainless
|
||||
Comment: 3B24 FD39 4732 D6A9 CF6E 071E 21AD CF3B 9F33 49FE
|
||||
Comment: Foo Bank Employee <employee@foobank.com>
|
||||
|
||||
lIYEYrcZeRYJKwYBBAHaRw8BAQdAl9j10kv/7NRAZe+yATf7kkXOoHwkxaKkHzM3
|
||||
wMihWlv+CQMCMzeQucBpShVg5GroEDGhC62uvl+Rjki+P6aR5rCKbaoff1Y3DEGQ
|
||||
LEz0InfGo4I2lLUhox1E+th6BHT+fwg7E/Qk+9j040TvEXAqHIJhXbQoRm9vIEJh
|
||||
bmsgRW1wbG95ZWUgPGVtcGxveWVlQGZvb2JhbmsuY29tPoiPBBMWCgBBBQJitxl5
|
||||
CRAhrc87nzNJ/hYhBDsk/TlHMtapz24HHiGtzzufM0n+Ap4BApsBBRYCAwEABAsJ
|
||||
CAcFFQoJCAsCmQEAADVoAQC2GKJVRxiIpXLN/NutkuRCVNY1Hp+7zl9Hms+ezY1R
|
||||
3AD/a+Sud4OaQTTiixHBQYsyHfSJDN25DdoVbSER7Y6YNQ6ciwRitxl5EgorBgEE
|
||||
AZdVAQUBAQdAKzB8vnErhsUnWUnBp3KMcwfWS4qMU0ZUeTKnfzNqzRgDAQgH/gkD
|
||||
AjM3kLnAaUoVYI0D8gPzG1t3dPpkNgc411bts2T9BgrZPkV7C3u338TXsFSHxTdo
|
||||
vK7/l/PhikN0qKjKVG5PXnxHhu56RUYiF+XAhkjcI9qIdQQYFgoAHQUCYrcZeQKe
|
||||
AQKbDAUWAgMBAAQLCQgHBRUKCQgLAAoJECGtzzufM0n+vDoA/A3p3LMYSUcvELY9
|
||||
Af3nC6/471r5B3MDnURShddOtcRyAP96J1i5ExKnC+Ltw36/QtyxfyHp9Pr9Fw6B
|
||||
X/zq4EFoCpyGBGK3GXkWCSsGAQQB2kcPAQEHQPlVJ9tosjkDY81zIKll52Gh00rh
|
||||
hXY1jFExx248kkrj/gkDAjM3kLnAaUoVYFXrAVkSWsFeXVYLYtq0a49sc5q7Kz1K
|
||||
KJmDBZKnvwgipPqf1eAkcZDSNanBZ2BO2K77Ix5FDFp67qc3XIZ6HUZqrP1siQmI
|
||||
1QQYFgoAfQUCYrcZeQKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmK3
|
||||
GXkACgkQnGOeWTe0K9zF6wEAh/nFBiBEV8N5CWvre6CU0Ky85cxrwE0vOA5tPaef
|
||||
wusBAL6yqhbJwtEV0WqDb0Tg6OFl3eJG8VYOgh+zrt33LAEOAAoJECGtzzufM0n+
|
||||
NYcA/jOzaTUy1XZ45yNTN23xx8yyIdwPb24my/o7rbyCpV2qAP929vks8rEU+d+F
|
||||
IY6KnSrZRKtsFkuC6kGOw25VW7YiAw==
|
||||
=kvmL
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
Loading…
Reference in a new issue