forked from I2P_Developers/i2p.i2p
Router: Add methods to verify and track members of our family;
use on sybil page
This commit is contained in:
@@ -4,6 +4,7 @@ import java.io.IOException;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.text.Collator;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -24,6 +25,7 @@ import net.i2p.data.router.RouterInfo;
|
|||||||
import net.i2p.data.router.RouterKeyGenerator;
|
import net.i2p.data.router.RouterKeyGenerator;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.router.TunnelPoolSettings;
|
import net.i2p.router.TunnelPoolSettings;
|
||||||
|
import net.i2p.router.crypto.FamilyKeyCrypto;
|
||||||
import net.i2p.router.peermanager.DBHistory;
|
import net.i2p.router.peermanager.DBHistory;
|
||||||
import net.i2p.router.peermanager.PeerProfile;
|
import net.i2p.router.peermanager.PeerProfile;
|
||||||
import net.i2p.router.tunnel.pool.TunnelPool;
|
import net.i2p.router.tunnel.pool.TunnelPool;
|
||||||
@@ -61,6 +63,8 @@ class SybilRenderer {
|
|||||||
private static final double POINTS_US24 = 25.0;
|
private static final double POINTS_US24 = 25.0;
|
||||||
private static final double POINTS_US16 = 10.0;
|
private static final double POINTS_US16 = 10.0;
|
||||||
private static final double POINTS_FAMILY = -2.0;
|
private static final double POINTS_FAMILY = -2.0;
|
||||||
|
private static final double POINTS_BAD_OUR_FAMILY = 100.0;
|
||||||
|
private static final double POINTS_OUR_FAMILY = -100.0;
|
||||||
private static final double MIN_CLOSE = 242.0;
|
private static final double MIN_CLOSE = 242.0;
|
||||||
private static final double PAIR_DISTANCE_FACTOR = 2.0;
|
private static final double PAIR_DISTANCE_FACTOR = 2.0;
|
||||||
private static final double OUR_KEY_FACTOR = 4.0;
|
private static final double OUR_KEY_FACTOR = 4.0;
|
||||||
@@ -370,6 +374,7 @@ class SybilRenderer {
|
|||||||
|
|
||||||
private static class FoofComparator implements Comparator<String>, Serializable {
|
private static class FoofComparator implements Comparator<String>, Serializable {
|
||||||
private final ObjectCounter<String> _o;
|
private final ObjectCounter<String> _o;
|
||||||
|
private final Collator _comp = Collator.getInstance();
|
||||||
public FoofComparator(ObjectCounter<String> o) { _o = o;}
|
public FoofComparator(ObjectCounter<String> o) { _o = o;}
|
||||||
public int compare(String l, String r) {
|
public int compare(String l, String r) {
|
||||||
// reverse by count
|
// reverse by count
|
||||||
@@ -377,7 +382,7 @@ class SybilRenderer {
|
|||||||
if (rv != 0)
|
if (rv != 0)
|
||||||
return rv;
|
return rv;
|
||||||
// foward by name
|
// foward by name
|
||||||
return l.compareTo(r);
|
return _comp.compare(l, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,6 +583,8 @@ class SybilRenderer {
|
|||||||
}
|
}
|
||||||
List<String> foo = new ArrayList<String>(oc.objects());
|
List<String> foo = new ArrayList<String>(oc.objects());
|
||||||
Collections.sort(foo, new FoofComparator(oc));
|
Collections.sort(foo, new FoofComparator(oc));
|
||||||
|
FamilyKeyCrypto fkc = _context.router().getFamilyKeyCrypto();
|
||||||
|
String ourFamily = fkc != null ? fkc.getOurFamilyName() : null;
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (String s : foo) {
|
for (String s : foo) {
|
||||||
int count = oc.count(s);
|
int count = oc.count(s);
|
||||||
@@ -593,12 +600,18 @@ class SybilRenderer {
|
|||||||
// limit display
|
// limit display
|
||||||
//renderRouterInfo(buf, info, null, false, false);
|
//renderRouterInfo(buf, info, null, false, false);
|
||||||
double point = POINTS_FAMILY;
|
double point = POINTS_FAMILY;
|
||||||
if (count > 1)
|
if (fkc != null && s.equals(ourFamily)) {
|
||||||
addPoints(points, info.getHash(), point, "Same declared family \"" + DataHelper.escapeHTML(s) + "\" with " + (count - 1) + " other" + (( count > 2) ? "s" : ""));
|
if (fkc.verifyOurFamily(info))
|
||||||
|
addPoints(points, info.getHash(), POINTS_OUR_FAMILY, "Our family \"" + DataHelper.escapeHTML(s) + "\" with " + (count - 1) + " other" + (( count > 2) ? "s" : ""));
|
||||||
else
|
else
|
||||||
|
addPoints(points, info.getHash(), POINTS_BAD_OUR_FAMILY, "Spoofed our family \"" + DataHelper.escapeHTML(s) + "\" with " + (count - 1) + " other" + (( count > 2) ? "s" : ""));
|
||||||
|
} else if (count > 1) {
|
||||||
|
addPoints(points, info.getHash(), point, "Same declared family \"" + DataHelper.escapeHTML(s) + "\" with " + (count - 1) + " other" + (( count > 2) ? "s" : ""));
|
||||||
|
} else {
|
||||||
addPoints(points, info.getHash(), point, "Declared family \"" + DataHelper.escapeHTML(s) + '"');
|
addPoints(points, info.getHash(), point, "Declared family \"" + DataHelper.escapeHTML(s) + '"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!found)
|
if (!found)
|
||||||
buf.append("<p>None</p>");
|
buf.append("<p>None</p>");
|
||||||
out.write(buf.toString());
|
out.write(buf.toString());
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.security.PrivateKey;
|
|||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.security.cert.X509CRL;
|
import java.security.cert.X509CRL;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -44,6 +45,7 @@ public class FamilyKeyCrypto {
|
|||||||
private final Log _log;
|
private final Log _log;
|
||||||
private final Map<Hash, String> _verified;
|
private final Map<Hash, String> _verified;
|
||||||
private final Set<Hash> _negativeCache;
|
private final Set<Hash> _negativeCache;
|
||||||
|
private final Set<Hash> _ourFamily;
|
||||||
// following for verification only, otherwise null
|
// following for verification only, otherwise null
|
||||||
private final String _fname;
|
private final String _fname;
|
||||||
private final SigningPrivateKey _privkey;
|
private final SigningPrivateKey _privkey;
|
||||||
@@ -82,13 +84,15 @@ public class FamilyKeyCrypto {
|
|||||||
_fname = _context.getProperty(PROP_FAMILY_NAME);
|
_fname = _context.getProperty(PROP_FAMILY_NAME);
|
||||||
if (_fname != null) {
|
if (_fname != null) {
|
||||||
if (_fname.contains("/") || _fname.contains("\\") ||
|
if (_fname.contains("/") || _fname.contains("\\") ||
|
||||||
_fname.contains("..") || (new File(_fname)).isAbsolute())
|
_fname.contains("..") || (new File(_fname)).isAbsolute() ||
|
||||||
throw new GeneralSecurityException("Illegal family name");
|
_fname.length() <= 0)
|
||||||
|
throw new GeneralSecurityException("Illegal family name: " + _fname);
|
||||||
}
|
}
|
||||||
_privkey = (_fname != null) ? initialize() : null;
|
_privkey = (_fname != null) ? initialize() : null;
|
||||||
_pubkey = (_privkey != null) ? _privkey.toPublic() : null;
|
_pubkey = (_privkey != null) ? _privkey.toPublic() : null;
|
||||||
_verified = new ConcurrentHashMap<Hash, String>(4);
|
_verified = new ConcurrentHashMap<Hash, String>(4);
|
||||||
_negativeCache = new ConcurrentHashSet<Hash>(4);
|
_negativeCache = new ConcurrentHashSet<Hash>(4);
|
||||||
|
_ourFamily = (_privkey != null) ? new ConcurrentHashSet<Hash>(4) : Collections.<Hash>emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -144,6 +148,35 @@ public class FamilyKeyCrypto {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do we have a valid family?
|
||||||
|
* @since 0.9.28
|
||||||
|
*/
|
||||||
|
public boolean hasFamily() {
|
||||||
|
return _pubkey != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get verified members of our family.
|
||||||
|
* Will not contain ourselves.
|
||||||
|
*
|
||||||
|
* @return non-null, not a copy, do not modify
|
||||||
|
* @since 0.9.28
|
||||||
|
*/
|
||||||
|
public Set<Hash> getOurFamily() {
|
||||||
|
return _ourFamily;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get our family name.
|
||||||
|
*
|
||||||
|
* @return name or null
|
||||||
|
* @since 0.9.28
|
||||||
|
*/
|
||||||
|
public String getOurFamilyName() {
|
||||||
|
return _fname;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify the family signature in a RouterInfo.
|
* Verify the family signature in a RouterInfo.
|
||||||
* @return true if good sig or if no family specified at all
|
* @return true if good sig or if no family specified at all
|
||||||
@@ -152,6 +185,44 @@ public class FamilyKeyCrypto {
|
|||||||
String name = ri.getOption(OPT_NAME);
|
String name = ri.getOption(OPT_NAME);
|
||||||
if (name == null)
|
if (name == null)
|
||||||
return true;
|
return true;
|
||||||
|
return verify(ri, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the family in a RouterInfo matches ours and the signature is good.
|
||||||
|
* Returns false if we don't have a family and sig, or they don't.
|
||||||
|
* Returns false for ourselves.
|
||||||
|
*
|
||||||
|
* @return true if family matches with good sig
|
||||||
|
* @since 0.9.28
|
||||||
|
*/
|
||||||
|
public boolean verifyOurFamily(RouterInfo ri) {
|
||||||
|
if (_pubkey == null)
|
||||||
|
return false;
|
||||||
|
String name = ri.getOption(OPT_NAME);
|
||||||
|
if (!_fname.equals(name))
|
||||||
|
return false;
|
||||||
|
Hash h = ri.getHash();
|
||||||
|
if (_ourFamily.contains(h))
|
||||||
|
return true;
|
||||||
|
if (h.equals(_context.routerHash()))
|
||||||
|
return false;
|
||||||
|
boolean rv = verify(ri, name);
|
||||||
|
if (rv) {
|
||||||
|
_ourFamily.add(h);
|
||||||
|
_log.logAlways(Log.INFO, "Found and verified member of our family (" + _fname + "): " + h);
|
||||||
|
} else {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Found spoofed member of our family (" + _fname + "): " + h);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the family in a RouterInfo, name already retrieved
|
||||||
|
* @since 0.9.28
|
||||||
|
*/
|
||||||
|
private boolean verify(RouterInfo ri, String name) {
|
||||||
Hash h = ri.getHash();
|
Hash h = ri.getHash();
|
||||||
String ssig = ri.getOption(OPT_SIG);
|
String ssig = ri.getOption(OPT_SIG);
|
||||||
if (ssig == null) {
|
if (ssig == null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user