// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.trustosm.util;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JOptionPane;
import org.bouncycastle.openpgp.PGPSignature;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.plugins.trustosm.TrustOSMplugin;
import org.openstreetmap.josm.plugins.trustosm.data.TrustNode;
import org.openstreetmap.josm.plugins.trustosm.data.TrustOsmPrimitive;
import org.openstreetmap.josm.plugins.trustosm.data.TrustSignatures;
import org.openstreetmap.josm.plugins.trustosm.data.TrustWay;
public final class TrustAnalyzer {
private TrustAnalyzer() {
// Hide default constructors for utilities classes
}
public static void showManipulationWarning() {
JOptionPane.showMessageDialog(Main.parent, tr("The Signature is broken!"), tr("Manipulation Warning"), JOptionPane.WARNING_MESSAGE);
}
public static double computeReputation(TrustOsmPrimitive trust, Map<String, List<PGPSignature>> textsigs) {
/** just for simplicity - count all valid sigs */
int count = 0;
for (List<PGPSignature> siglist : textsigs.values()) {
count += siglist.size();
}
return count;
}
public static boolean isTagRatingValid(TrustOsmPrimitive trust, String key, String signedPlaintext) {
/** Rating is valid if signed plaintext matches the current plaintext */
String currentSigtext = TrustOsmPrimitive.generateTagSigtext(trust.getOsmPrimitive(), key);
return currentSigtext.equals(signedPlaintext);
}
public static void checkTag(TrustOsmPrimitive trust, String key) {
Map<String, List<PGPSignature>> validRatings = new HashMap<>();
TrustSignatures sigs;
if ((sigs = trust.getSigsOnKey(key)) != null) {
for (PGPSignature sig : sigs.getSignatures()) {
/** Here we have a full rating
* The first question: Is the Signature valid?
* It could be manipulated...
* */
String signedPlaintext = sigs.getSigtext(sig);
if (TrustOSMplugin.gpg.verify(signedPlaintext, sig)) {
/** If it is valid...
* Second question: Is the rating valid?
*/
if (isTagRatingValid(trust, key, signedPlaintext)) {
/** if the rating is good, we can try to compute a reputation value at the end
* so we save the important rating stuff
*/
if (validRatings.containsKey(signedPlaintext)) {
validRatings.get(signedPlaintext).add(sig);
} else {
List<PGPSignature> l = new ArrayList<>();
l.add(sig);
validRatings.put(signedPlaintext, l);
}
//if (sigs.getStatus() == TrustSignatures.SIG_UNKNOWN) sigs.setStatus(TrustSignatures.SIG_VALID);
} /*else {
sigs.setStatus(TrustSignatures.SIG_BROKEN);
}*/
} else {
//sigs.setStatus(TrustSignatures.SIG_BROKEN);
showManipulationWarning();
}
}
/** now we know which ratings are valid to compute a reputation */
sigs.setReputation(computeReputation(trust, validRatings));
/** if all available signatures are valid we can set the TrustSignatures status to valid */
System.out.println(validRatings.size()+":"+sigs.countSigs());
if (validRatings.size() == 1) sigs.setStatus(TrustSignatures.SIG_VALID);
else sigs.setStatus(TrustSignatures.SIG_BROKEN);
}
}
public static boolean isNodeRatingValid(TrustNode trust, String signedPlaintext, PGPSignature sig) {
/** Rating is valid if Node from signed plaintext is inside Tolerance given in Signature */
Node signedNode = TrustNode.generateNodeFromSigtext(signedPlaintext);
Node currentNode = (Node) trust.getOsmPrimitive();
double dist = signedNode.getCoor().greatCircleDistance(currentNode.getCoor());
/** is distance between signed Node and current Node inside tolerance? */
return dist <= TrustGPG.searchTolerance(sig);
}
/**
* Check if the ratings made for a Node are valid for the current position of that node
* and compute reputation.
* @param trust The current TrustNode with its ratings
*/
public static void checkNode(TrustNode trust) {
Map<String, List<PGPSignature>> validRatings = new HashMap<>();
TrustSignatures sigs;
if ((sigs = trust.getNodeSigs()) != null) {
for (String signedPlaintext : sigs.getAllPlainTexts()) {
for (PGPSignature sig : sigs.getSignaturesByPlaintext(signedPlaintext)) {
/** first thing: check signature */
if (TrustOSMplugin.gpg.verify(signedPlaintext, sig)) {
/** if signature is valid check rating */
if (isNodeRatingValid(trust, signedPlaintext, sig)) {
/** if the rating is good, we can try to compute a reputation value at the end
* so we save the important rating stuff
*/
if (validRatings.containsKey(signedPlaintext)) {
validRatings.get(signedPlaintext).add(sig);
} else {
List<PGPSignature> l = new ArrayList<>();
l.add(sig);
validRatings.put(signedPlaintext, l);
}
//if (sigs.getStatus() == TrustSignatures.SIG_UNKNOWN) sigs.setStatus(TrustSignatures.SIG_VALID);
} /*else {
sigs.setStatus(TrustSignatures.SIG_BROKEN);
}*/
} else {
//sigs.setStatus(TrustSignatures.SIG_BROKEN);
showManipulationWarning();
}
}
}
/** now we know which ratings are valid to compute a reputation */
sigs.setReputation(computeReputation(trust, validRatings));
/** if all available signatures are valid we can set the TrustSignatures status to valid */
if (validRatings.size() == 1) sigs.setStatus(TrustSignatures.SIG_VALID);
else sigs.setStatus(TrustSignatures.SIG_BROKEN);
}
}
/**
* Check if the ratings made for a specific WaySegment are valid for the current form of that WaySegment
*/
public static boolean isSegmentRatingValid(TrustWay trust, List<Node> nodes, String signedPlaintext, PGPSignature sig) {
/** Rating is valid if Nodes from Segment of signed plaintext are inside Tolerance given in Signature */
List<Node> signedSegment = TrustWay.generateSegmentFromSigtext(signedPlaintext);
double tolerance = TrustGPG.searchTolerance(sig);
for (int i = 0; i < 2; i++) {
Node signedNode = signedSegment.get(i);
Node currentNode = nodes.get(i);
double dist = signedNode.getCoor().greatCircleDistance(currentNode.getCoor());
if (dist > tolerance) return false;
}
return true;
}
/**
* Check if there are ratings for a current WaySegment of a TrustWay
* and if so, compute Reputation
* @param trust the current TrustWay
* @param seg the current WaySegment to check for reputation
*/
public static void checkSegment(TrustWay trust, List<Node> nodes) {
Map<String, List<PGPSignature>> validRatings = new HashMap<>();
TrustSignatures sigs;
if ((sigs = trust.getSigsOnSegment(nodes)) != null) {
for (String signedPlaintext : sigs.getAllPlainTexts()) {
for (PGPSignature sig : sigs.getSignaturesByPlaintext(signedPlaintext)) {
/** first thing: check signature */
if (TrustOSMplugin.gpg.verify(signedPlaintext, sig)) {
/** if signature is valid check rating */
if (isSegmentRatingValid(trust, nodes, signedPlaintext, sig)) {
/** if the rating is good, we can try to compute a reputation value at the end
* so we save the important rating stuff
*/
if (validRatings.containsKey(signedPlaintext)) {
validRatings.get(signedPlaintext).add(sig);
} else {
List<PGPSignature> l = new ArrayList<>();
l.add(sig);
validRatings.put(signedPlaintext, l);
}
//if (sigs.getStatus() == TrustSignatures.SIG_UNKNOWN) sigs.setStatus(TrustSignatures.SIG_VALID);
} /*else {
sigs.setStatus(TrustSignatures.SIG_BROKEN);
}*/
} else {
//sigs.setStatus(TrustSignatures.SIG_BROKEN);
showManipulationWarning();
}
}
}
/** now we know which ratings are valid to compute a reputation */
sigs.setReputation(computeReputation(trust, validRatings));
/** if all available signatures are valid we can set the TrustSignatures status to valid */
if (validRatings.size() == sigs.countSigs()) sigs.setStatus(TrustSignatures.SIG_VALID);
else sigs.setStatus(TrustSignatures.SIG_BROKEN);
}
}
public static void checkEverything(TrustOsmPrimitive trust) {
/** check every single tag for reputation */
for (String key : trust.getSignedKeys()) {
checkTag(trust, key);
}
if (trust instanceof TrustNode) {
/** check all reputation of this single Node */
checkNode((TrustNode) trust);
} else if (trust instanceof TrustWay) {
TrustWay tw = (TrustWay) trust;
/** check all reputation for every Segment of this Way */
List<Node> wayNodes = ((Way) tw.getOsmPrimitive()).getNodes();
for (int i = 0; i < wayNodes.size()-1; i++) {
List<Node> nodes = new ArrayList<>();
nodes.add(wayNodes.get(i));
nodes.add(wayNodes.get(i+1));
checkSegment(tw, nodes);
}
} /*else if (trust instanceof TrustRelation) {
TrustRelation tr = (TrustRelation) trust;
}*/
}
}