forked from I2P_Developers/i2p.i2p
RoutingKeyGenerator:
- Move from core to RouterKeyGenerator in router.jar - Leave RoutingKeyGenerator as a simple abstract class - DatabaseEntry now uses timestamp instead of mod data to determine if mod data has changed. Don't expose mod data to DatabaseEntry any more. - I2PAppContext.routingKeyGenerator() now returns null; you must be in RouterContext to get a generator.
This commit is contained in:
@ -199,10 +199,10 @@ public class NetDbRenderer {
|
|||||||
FloodfillNetworkDatabaseFacade netdb = (FloodfillNetworkDatabaseFacade)_context.netDb();
|
FloodfillNetworkDatabaseFacade netdb = (FloodfillNetworkDatabaseFacade)_context.netDb();
|
||||||
buf.append("<p><b>Total Leasesets: ").append(leases.size());
|
buf.append("<p><b>Total Leasesets: ").append(leases.size());
|
||||||
buf.append("</b></p><p><b>Published (RAP) Leasesets: ").append(netdb.getKnownLeaseSets());
|
buf.append("</b></p><p><b>Published (RAP) Leasesets: ").append(netdb.getKnownLeaseSets());
|
||||||
buf.append("</b></p><p><b>Mod Data: \"").append(DataHelper.getUTF8(_context.routingKeyGenerator().getModData()))
|
buf.append("</b></p><p><b>Mod Data: \"").append(DataHelper.getUTF8(_context.routerKeyGenerator().getModData()))
|
||||||
.append("\" Last Changed: ").append(new Date(_context.routingKeyGenerator().getLastChanged()));
|
.append("\" Last Changed: ").append(new Date(_context.routerKeyGenerator().getLastChanged()));
|
||||||
buf.append("</b></p><p><b>Next Mod Data: \"").append(DataHelper.getUTF8(_context.routingKeyGenerator().getNextModData()))
|
buf.append("</b></p><p><b>Next Mod Data: \"").append(DataHelper.getUTF8(_context.routerKeyGenerator().getNextModData()))
|
||||||
.append("\" Change in: ").append(DataHelper.formatDuration(_context.routingKeyGenerator().getTimeTillMidnight()));
|
.append("\" Change in: ").append(DataHelper.formatDuration(_context.routerKeyGenerator().getTimeTillMidnight()));
|
||||||
int ff = _context.peerManager().getPeersByCapability(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL).size();
|
int ff = _context.peerManager().getPeersByCapability(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL).size();
|
||||||
buf.append("</b></p><p><b>Known Floodfills: ").append(ff);
|
buf.append("</b></p><p><b>Known Floodfills: ").append(ff);
|
||||||
buf.append("</b></p><p><b>Currently Floodfill? ");
|
buf.append("</b></p><p><b>Currently Floodfill? ");
|
||||||
|
@ -86,7 +86,6 @@ public class I2PAppContext {
|
|||||||
private SHA256Generator _sha;
|
private SHA256Generator _sha;
|
||||||
protected Clock _clock; // overridden in RouterContext
|
protected Clock _clock; // overridden in RouterContext
|
||||||
private DSAEngine _dsa;
|
private DSAEngine _dsa;
|
||||||
private RoutingKeyGenerator _routingKeyGenerator;
|
|
||||||
private RandomSource _random;
|
private RandomSource _random;
|
||||||
private KeyGenerator _keyGenerator;
|
private KeyGenerator _keyGenerator;
|
||||||
protected KeyRing _keyRing; // overridden in RouterContext
|
protected KeyRing _keyRing; // overridden in RouterContext
|
||||||
@ -106,7 +105,6 @@ public class I2PAppContext {
|
|||||||
private volatile boolean _shaInitialized;
|
private volatile boolean _shaInitialized;
|
||||||
protected volatile boolean _clockInitialized; // used in RouterContext
|
protected volatile boolean _clockInitialized; // used in RouterContext
|
||||||
private volatile boolean _dsaInitialized;
|
private volatile boolean _dsaInitialized;
|
||||||
private volatile boolean _routingKeyGeneratorInitialized;
|
|
||||||
private volatile boolean _randomInitialized;
|
private volatile boolean _randomInitialized;
|
||||||
private volatile boolean _keyGeneratorInitialized;
|
private volatile boolean _keyGeneratorInitialized;
|
||||||
protected volatile boolean _keyRingInitialized; // used in RouterContext
|
protected volatile boolean _keyRingInitialized; // used in RouterContext
|
||||||
@ -126,7 +124,7 @@ public class I2PAppContext {
|
|||||||
private final Object _lock1 = new Object(), _lock2 = new Object(), _lock3 = new Object(), _lock4 = new Object(),
|
private final Object _lock1 = new Object(), _lock2 = new Object(), _lock3 = new Object(), _lock4 = new Object(),
|
||||||
_lock5 = new Object(), _lock6 = new Object(), _lock7 = new Object(), _lock8 = new Object(),
|
_lock5 = new Object(), _lock6 = new Object(), _lock7 = new Object(), _lock8 = new Object(),
|
||||||
_lock9 = new Object(), _lock10 = new Object(), _lock11 = new Object(), _lock12 = new Object(),
|
_lock9 = new Object(), _lock10 = new Object(), _lock11 = new Object(), _lock12 = new Object(),
|
||||||
_lock13 = new Object(), _lock14 = new Object(), _lock15 = new Object(), _lock16 = new Object(),
|
_lock13 = new Object(), _lock14 = new Object(), _lock16 = new Object(),
|
||||||
_lock17 = new Object(), _lock18 = new Object(), _lock19 = new Object(), _lock20 = new Object();
|
_lock17 = new Object(), _lock18 = new Object(), _lock19 = new Object(), _lock20 = new Object();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -851,19 +849,13 @@ public class I2PAppContext {
|
|||||||
* may want to test out how things react when peers don't agree on
|
* may want to test out how things react when peers don't agree on
|
||||||
* how to skew.
|
* how to skew.
|
||||||
*
|
*
|
||||||
|
* As of 0.9.16, returns null in I2PAppContext.
|
||||||
|
* You must be in RouterContext to get a generator.
|
||||||
|
*
|
||||||
|
* @return null always
|
||||||
*/
|
*/
|
||||||
public RoutingKeyGenerator routingKeyGenerator() {
|
public RoutingKeyGenerator routingKeyGenerator() {
|
||||||
if (!_routingKeyGeneratorInitialized)
|
return null;
|
||||||
initializeRoutingKeyGenerator();
|
|
||||||
return _routingKeyGenerator;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeRoutingKeyGenerator() {
|
|
||||||
synchronized (_lock15) {
|
|
||||||
if (_routingKeyGenerator == null)
|
|
||||||
_routingKeyGenerator = new RoutingKeyGenerator(this);
|
|
||||||
_routingKeyGeneratorInitialized = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,6 +11,7 @@ package net.i2p.data;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.crypto.DSAEngine;
|
import net.i2p.crypto.DSAEngine;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +48,7 @@ public abstract class DatabaseEntry extends DataStructureImpl {
|
|||||||
|
|
||||||
protected volatile Signature _signature;
|
protected volatile Signature _signature;
|
||||||
protected volatile Hash _currentRoutingKey;
|
protected volatile Hash _currentRoutingKey;
|
||||||
protected volatile byte[] _routingKeyGenMod;
|
protected volatile long _routingKeyGenMod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A common interface to the timestamp of the two subclasses.
|
* A common interface to the timestamp of the two subclasses.
|
||||||
@ -106,11 +107,15 @@ public abstract class DatabaseEntry extends DataStructureImpl {
|
|||||||
* Get the routing key for the structure using the current modifier in the RoutingKeyGenerator.
|
* Get the routing key for the structure using the current modifier in the RoutingKeyGenerator.
|
||||||
* This only calculates a new one when necessary though (if the generator's key modifier changes)
|
* This only calculates a new one when necessary though (if the generator's key modifier changes)
|
||||||
*
|
*
|
||||||
|
* @throws IllegalStateException if not in RouterContext
|
||||||
*/
|
*/
|
||||||
public Hash getRoutingKey() {
|
public Hash getRoutingKey() {
|
||||||
RoutingKeyGenerator gen = RoutingKeyGenerator.getInstance();
|
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||||
byte[] mod = gen.getModData();
|
if (!ctx.isRouterContext())
|
||||||
if (!Arrays.equals(mod, _routingKeyGenMod)) {
|
throw new IllegalStateException("Not in router context");
|
||||||
|
RoutingKeyGenerator gen = ctx.routingKeyGenerator();
|
||||||
|
long mod = gen.getLastChanged();
|
||||||
|
if (mod != _routingKeyGenMod) {
|
||||||
_currentRoutingKey = gen.getRoutingKey(getHash());
|
_currentRoutingKey = gen.getRoutingKey(getHash());
|
||||||
_routingKeyGenMod = mod;
|
_routingKeyGenMod = mod;
|
||||||
}
|
}
|
||||||
@ -124,9 +129,16 @@ public abstract class DatabaseEntry extends DataStructureImpl {
|
|||||||
_currentRoutingKey = key;
|
_currentRoutingKey = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IllegalStateException if not in RouterContext
|
||||||
|
*/
|
||||||
public boolean validateRoutingKey() {
|
public boolean validateRoutingKey() {
|
||||||
|
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||||
|
if (!ctx.isRouterContext())
|
||||||
|
throw new IllegalStateException("Not in router context");
|
||||||
|
RoutingKeyGenerator gen = ctx.routingKeyGenerator();
|
||||||
Hash destKey = getHash();
|
Hash destKey = getHash();
|
||||||
Hash rk = RoutingKeyGenerator.getInstance().getRoutingKey(destKey);
|
Hash rk = gen.getRoutingKey(destKey);
|
||||||
return rk.equals(getRoutingKey());
|
return rk.equals(getRoutingKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,215 +9,40 @@ package net.i2p.data;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.GregorianCalendar;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.crypto.SHA256Generator;
|
|
||||||
import net.i2p.util.HexDump;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to manage the munging of hashes into routing keys - given a hash,
|
* Component to manage the munging of hashes into routing keys - given a hash,
|
||||||
* perform some consistent transformation against it and return the result.
|
* perform some consistent transformation against it and return the result.
|
||||||
* This transformation is fed by the current "mod data".
|
* This transformation is fed by the current "mod data".
|
||||||
*
|
*
|
||||||
* Right now the mod data is the current date (GMT) as a string: "yyyyMMdd",
|
* As of 0.9.16, this is essentially just an interface.
|
||||||
* and the transformation takes the original hash, appends the bytes of that mod data,
|
* Implementation moved to net.i2p.data.router.RouterKeyGenerator.
|
||||||
* then returns the SHA256 of that concatenation.
|
* No generator is available in I2PAppContext; you must be in RouterContext.
|
||||||
*
|
|
||||||
* Do we want this to simply do the XOR of the SHA256 of the current mod data and
|
|
||||||
* the key? does that provide the randomization we need? It'd save an SHA256 op.
|
|
||||||
* Bah, too much effort to think about for so little gain. Other algorithms may come
|
|
||||||
* into play layer on about making periodic updates to the routing key for data elements
|
|
||||||
* to mess with Sybil. This may be good enough though.
|
|
||||||
*
|
|
||||||
* Also - the method generateDateBasedModData() should be called after midnight GMT
|
|
||||||
* once per day to generate the correct routing keys!
|
|
||||||
*
|
|
||||||
* Warning - API subject to change. Not for use outside the router.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RoutingKeyGenerator {
|
public abstract class RoutingKeyGenerator {
|
||||||
private final Log _log;
|
|
||||||
private final I2PAppContext _context;
|
|
||||||
|
|
||||||
public RoutingKeyGenerator(I2PAppContext context) {
|
|
||||||
_log = context.logManager().getLog(RoutingKeyGenerator.class);
|
|
||||||
_context = context;
|
|
||||||
// ensure non-null mod data
|
|
||||||
generateDateBasedModData();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the generator for this context.
|
||||||
|
*
|
||||||
|
* @return null in I2PAppContext; non-null in RouterContext.
|
||||||
|
*/
|
||||||
public static RoutingKeyGenerator getInstance() {
|
public static RoutingKeyGenerator getInstance() {
|
||||||
return I2PAppContext.getGlobalContext().routingKeyGenerator();
|
return I2PAppContext.getGlobalContext().routingKeyGenerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
private volatile byte _currentModData[];
|
|
||||||
private volatile byte _nextModData[];
|
|
||||||
private volatile long _nextMidnight;
|
|
||||||
private volatile long _lastChanged;
|
|
||||||
|
|
||||||
private final static Calendar _cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
|
|
||||||
private static final String FORMAT = "yyyyMMdd";
|
|
||||||
private static final int LENGTH = FORMAT.length();
|
|
||||||
private final static SimpleDateFormat _fmt = new SimpleDateFormat(FORMAT, Locale.US);
|
|
||||||
static {
|
|
||||||
// make sure GMT is set, azi2phelper Vuze plugin is disabling static JVM TZ setting in Router.java
|
|
||||||
_fmt.setCalendar(_cal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current (today's) mod data.
|
* The version of the current (today's) mod data.
|
||||||
* Warning - not a copy, do not corrupt.
|
* Use to determine if the routing key should be regenerated.
|
||||||
*
|
|
||||||
* @return non-null, 8 bytes
|
|
||||||
*/
|
*/
|
||||||
public byte[] getModData() {
|
public abstract long getLastChanged();
|
||||||
return _currentModData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tomorrow's mod data.
|
* Get the routing key for a key.
|
||||||
* Warning - not a copy, do not corrupt.
|
|
||||||
* For debugging use only.
|
|
||||||
*
|
|
||||||
* @return non-null, 8 bytes
|
|
||||||
* @since 0.9.10
|
|
||||||
*/
|
|
||||||
public byte[] getNextModData() {
|
|
||||||
return _nextModData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLastChanged() {
|
|
||||||
return _lastChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How long until midnight (ms)
|
|
||||||
*
|
|
||||||
* @return could be slightly negative
|
|
||||||
* @since 0.9.10 moved from UpdateRoutingKeyModifierJob
|
|
||||||
*/
|
|
||||||
public long getTimeTillMidnight() {
|
|
||||||
return _nextMidnight - _context.clock().now();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set _cal to midnight for the time given.
|
|
||||||
* Caller must synch.
|
|
||||||
* @since 0.9.10
|
|
||||||
*/
|
|
||||||
private void setCalToPreviousMidnight(long now) {
|
|
||||||
_cal.setTime(new Date(now));
|
|
||||||
_cal.set(Calendar.YEAR, _cal.get(Calendar.YEAR)); // gcj <= 4.0 workaround
|
|
||||||
_cal.set(Calendar.DAY_OF_YEAR, _cal.get(Calendar.DAY_OF_YEAR)); // gcj <= 4.0 workaround
|
|
||||||
_cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
||||||
_cal.set(Calendar.MINUTE, 0);
|
|
||||||
_cal.set(Calendar.SECOND, 0);
|
|
||||||
_cal.set(Calendar.MILLISECOND, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate mod data from _cal.
|
|
||||||
* Caller must synch.
|
|
||||||
* @since 0.9.10
|
|
||||||
*/
|
|
||||||
private byte[] generateModDataFromCal() {
|
|
||||||
Date today = _cal.getTime();
|
|
||||||
|
|
||||||
String modVal = _fmt.format(today);
|
|
||||||
if (modVal.length() != LENGTH)
|
|
||||||
throw new IllegalStateException();
|
|
||||||
byte[] mod = new byte[LENGTH];
|
|
||||||
for (int i = 0; i < LENGTH; i++)
|
|
||||||
mod[i] = (byte)(modVal.charAt(i) & 0xFF);
|
|
||||||
return mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the current modifier data with some bytes derived from the current
|
|
||||||
* date (yyyyMMdd in GMT)
|
|
||||||
*
|
|
||||||
* @return true if changed
|
|
||||||
*/
|
|
||||||
public synchronized boolean generateDateBasedModData() {
|
|
||||||
long now = _context.clock().now();
|
|
||||||
setCalToPreviousMidnight(now);
|
|
||||||
byte[] mod = generateModDataFromCal();
|
|
||||||
boolean changed = !Arrays.equals(_currentModData, mod);
|
|
||||||
if (changed) {
|
|
||||||
// add a day and store next midnight and mod data for convenience
|
|
||||||
_cal.add(Calendar.DATE, 1);
|
|
||||||
_nextMidnight = _cal.getTime().getTime();
|
|
||||||
byte[] next = generateModDataFromCal();
|
|
||||||
_currentModData = mod;
|
|
||||||
_nextModData = next;
|
|
||||||
_lastChanged = now;
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Routing modifier generated: " + HexDump.dump(mod));
|
|
||||||
}
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a modified (yet consistent) hash from the origKey by generating the
|
|
||||||
* SHA256 of the targetKey with the current modData appended to it
|
|
||||||
*
|
|
||||||
* This makes Sybil's job a lot harder, as she needs to essentially take over the
|
|
||||||
* whole keyspace.
|
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException if origKey is null
|
* @throws IllegalArgumentException if origKey is null
|
||||||
*/
|
*/
|
||||||
public Hash getRoutingKey(Hash origKey) {
|
public abstract Hash getRoutingKey(Hash origKey);
|
||||||
return getKey(origKey, _currentModData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the routing key using tomorrow's modData, not today's
|
|
||||||
*
|
|
||||||
* @since 0.9.10
|
|
||||||
*/
|
|
||||||
public Hash getNextRoutingKey(Hash origKey) {
|
|
||||||
return getKey(origKey, _nextModData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a modified (yet consistent) hash from the origKey by generating the
|
|
||||||
* SHA256 of the targetKey with the specified modData appended to it
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if origKey is null
|
|
||||||
*/
|
|
||||||
private static Hash getKey(Hash origKey, byte[] modData) {
|
|
||||||
if (origKey == null) throw new IllegalArgumentException("Original key is null");
|
|
||||||
byte modVal[] = new byte[Hash.HASH_LENGTH + LENGTH];
|
|
||||||
System.arraycopy(origKey.getData(), 0, modVal, 0, Hash.HASH_LENGTH);
|
|
||||||
System.arraycopy(modData, 0, modVal, Hash.HASH_LENGTH, LENGTH);
|
|
||||||
return SHA256Generator.getInstance().calculateHash(modVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/****
|
|
||||||
public static void main(String args[]) {
|
|
||||||
Hash k1 = new Hash();
|
|
||||||
byte k1d[] = new byte[Hash.HASH_LENGTH];
|
|
||||||
RandomSource.getInstance().nextBytes(k1d);
|
|
||||||
k1.setData(k1d);
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
System.out.println("K1: " + k1);
|
|
||||||
Hash k1m = RoutingKeyGenerator.getInstance().getRoutingKey(k1);
|
|
||||||
System.out.println("MOD: " + new String(RoutingKeyGenerator.getInstance().getModData()));
|
|
||||||
System.out.println("K1M: " + k1m);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Thread.sleep(2000);
|
|
||||||
} catch (Throwable t) { // nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
****/
|
|
||||||
}
|
}
|
||||||
|
224
router/java/src/net/i2p/data/router/RouterKeyGenerator.java
Normal file
224
router/java/src/net/i2p/data/router/RouterKeyGenerator.java
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
package net.i2p.data.router;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free (adj.): unencumbered; not under the control of others
|
||||||
|
* Written by jrandom in 2003 and released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
* It probably won't make your computer catch on fire, or eat
|
||||||
|
* your children, but it might. Use at your own risk.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.crypto.SHA256Generator;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.RoutingKeyGenerator;
|
||||||
|
import net.i2p.util.HexDump;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to manage the munging of hashes into routing keys - given a hash,
|
||||||
|
* perform some consistent transformation against it and return the result.
|
||||||
|
* This transformation is fed by the current "mod data".
|
||||||
|
*
|
||||||
|
* Right now the mod data is the current date (GMT) as a string: "yyyyMMdd",
|
||||||
|
* and the transformation takes the original hash, appends the bytes of that mod data,
|
||||||
|
* then returns the SHA256 of that concatenation.
|
||||||
|
*
|
||||||
|
* Do we want this to simply do the XOR of the SHA256 of the current mod data and
|
||||||
|
* the key? does that provide the randomization we need? It'd save an SHA256 op.
|
||||||
|
* Bah, too much effort to think about for so little gain. Other algorithms may come
|
||||||
|
* into play layer on about making periodic updates to the routing key for data elements
|
||||||
|
* to mess with Sybil. This may be good enough though.
|
||||||
|
*
|
||||||
|
* Also - the method generateDateBasedModData() should be called after midnight GMT
|
||||||
|
* once per day to generate the correct routing keys!
|
||||||
|
*
|
||||||
|
* @since 0.9.16 moved from net.i2p.data.RoutingKeyGenerator..
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class RouterKeyGenerator extends RoutingKeyGenerator {
|
||||||
|
private final Log _log;
|
||||||
|
private final I2PAppContext _context;
|
||||||
|
|
||||||
|
public RouterKeyGenerator(I2PAppContext context) {
|
||||||
|
_log = context.logManager().getLog(RoutingKeyGenerator.class);
|
||||||
|
_context = context;
|
||||||
|
// ensure non-null mod data
|
||||||
|
generateDateBasedModData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private volatile byte _currentModData[];
|
||||||
|
private volatile byte _nextModData[];
|
||||||
|
private volatile long _nextMidnight;
|
||||||
|
private volatile long _lastChanged;
|
||||||
|
|
||||||
|
private final static Calendar _cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||||
|
private static final String FORMAT = "yyyyMMdd";
|
||||||
|
private static final int LENGTH = FORMAT.length();
|
||||||
|
private final static SimpleDateFormat _fmt = new SimpleDateFormat(FORMAT, Locale.US);
|
||||||
|
static {
|
||||||
|
// make sure GMT is set, azi2phelper Vuze plugin is disabling static JVM TZ setting in Router.java
|
||||||
|
_fmt.setCalendar(_cal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current (today's) mod data.
|
||||||
|
* Warning - not a copy, do not corrupt.
|
||||||
|
*
|
||||||
|
* @return non-null, 8 bytes
|
||||||
|
*/
|
||||||
|
public byte[] getModData() {
|
||||||
|
return _currentModData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tomorrow's mod data.
|
||||||
|
* Warning - not a copy, do not corrupt.
|
||||||
|
* For debugging use only.
|
||||||
|
*
|
||||||
|
* @return non-null, 8 bytes
|
||||||
|
* @since 0.9.10
|
||||||
|
*/
|
||||||
|
public byte[] getNextModData() {
|
||||||
|
return _nextModData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastChanged() {
|
||||||
|
return _lastChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long until midnight (ms)
|
||||||
|
*
|
||||||
|
* @return could be slightly negative
|
||||||
|
* @since 0.9.10 moved from UpdateRoutingKeyModifierJob
|
||||||
|
*/
|
||||||
|
public long getTimeTillMidnight() {
|
||||||
|
return _nextMidnight - _context.clock().now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set _cal to midnight for the time given.
|
||||||
|
* Caller must synch.
|
||||||
|
* @since 0.9.10
|
||||||
|
*/
|
||||||
|
private void setCalToPreviousMidnight(long now) {
|
||||||
|
_cal.setTime(new Date(now));
|
||||||
|
_cal.set(Calendar.YEAR, _cal.get(Calendar.YEAR)); // gcj <= 4.0 workaround
|
||||||
|
_cal.set(Calendar.DAY_OF_YEAR, _cal.get(Calendar.DAY_OF_YEAR)); // gcj <= 4.0 workaround
|
||||||
|
_cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
_cal.set(Calendar.MINUTE, 0);
|
||||||
|
_cal.set(Calendar.SECOND, 0);
|
||||||
|
_cal.set(Calendar.MILLISECOND, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate mod data from _cal.
|
||||||
|
* Caller must synch.
|
||||||
|
* @since 0.9.10
|
||||||
|
*/
|
||||||
|
private byte[] generateModDataFromCal() {
|
||||||
|
Date today = _cal.getTime();
|
||||||
|
|
||||||
|
String modVal = _fmt.format(today);
|
||||||
|
if (modVal.length() != LENGTH)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
byte[] mod = new byte[LENGTH];
|
||||||
|
for (int i = 0; i < LENGTH; i++)
|
||||||
|
mod[i] = (byte)(modVal.charAt(i) & 0xFF);
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the current modifier data with some bytes derived from the current
|
||||||
|
* date (yyyyMMdd in GMT)
|
||||||
|
*
|
||||||
|
* @return true if changed
|
||||||
|
*/
|
||||||
|
public synchronized boolean generateDateBasedModData() {
|
||||||
|
long now = _context.clock().now();
|
||||||
|
setCalToPreviousMidnight(now);
|
||||||
|
byte[] mod = generateModDataFromCal();
|
||||||
|
boolean changed = !Arrays.equals(_currentModData, mod);
|
||||||
|
if (changed) {
|
||||||
|
// add a day and store next midnight and mod data for convenience
|
||||||
|
_cal.add(Calendar.DATE, 1);
|
||||||
|
_nextMidnight = _cal.getTime().getTime();
|
||||||
|
byte[] next = generateModDataFromCal();
|
||||||
|
_currentModData = mod;
|
||||||
|
_nextModData = next;
|
||||||
|
// ensure version is bumped
|
||||||
|
if (_lastChanged == now)
|
||||||
|
now++;
|
||||||
|
_lastChanged = now;
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Routing modifier generated: " + HexDump.dump(mod));
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a modified (yet consistent) hash from the origKey by generating the
|
||||||
|
* SHA256 of the targetKey with the current modData appended to it
|
||||||
|
*
|
||||||
|
* This makes Sybil's job a lot harder, as she needs to essentially take over the
|
||||||
|
* whole keyspace.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if origKey is null
|
||||||
|
*/
|
||||||
|
public Hash getRoutingKey(Hash origKey) {
|
||||||
|
return getKey(origKey, _currentModData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the routing key using tomorrow's modData, not today's
|
||||||
|
*
|
||||||
|
* @since 0.9.10
|
||||||
|
*/
|
||||||
|
public Hash getNextRoutingKey(Hash origKey) {
|
||||||
|
return getKey(origKey, _nextModData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a modified (yet consistent) hash from the origKey by generating the
|
||||||
|
* SHA256 of the targetKey with the specified modData appended to it
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if origKey is null
|
||||||
|
*/
|
||||||
|
private static Hash getKey(Hash origKey, byte[] modData) {
|
||||||
|
if (origKey == null) throw new IllegalArgumentException("Original key is null");
|
||||||
|
byte modVal[] = new byte[Hash.HASH_LENGTH + LENGTH];
|
||||||
|
System.arraycopy(origKey.getData(), 0, modVal, 0, Hash.HASH_LENGTH);
|
||||||
|
System.arraycopy(modData, 0, modVal, Hash.HASH_LENGTH, LENGTH);
|
||||||
|
return SHA256Generator.getInstance().calculateHash(modVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****
|
||||||
|
public static void main(String args[]) {
|
||||||
|
Hash k1 = new Hash();
|
||||||
|
byte k1d[] = new byte[Hash.HASH_LENGTH];
|
||||||
|
RandomSource.getInstance().nextBytes(k1d);
|
||||||
|
k1.setData(k1d);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
System.out.println("K1: " + k1);
|
||||||
|
Hash k1m = RoutingKeyGenerator.getInstance().getRoutingKey(k1);
|
||||||
|
System.out.println("MOD: " + new String(RoutingKeyGenerator.getInstance().getModData()));
|
||||||
|
System.out.println("K1M: " + k1m);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000);
|
||||||
|
} catch (Throwable t) { // nop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
****/
|
||||||
|
}
|
@ -1070,7 +1070,7 @@ public class Router implements RouterClock.ClockShiftListener {
|
|||||||
return;
|
return;
|
||||||
_eventLog.addEvent(EventLog.CLOCK_SHIFT, Long.toString(delta));
|
_eventLog.addEvent(EventLog.CLOCK_SHIFT, Long.toString(delta));
|
||||||
// update the routing key modifier
|
// update the routing key modifier
|
||||||
_context.routingKeyGenerator().generateDateBasedModData();
|
_context.routerKeyGenerator().generateDateBasedModData();
|
||||||
if (_context.commSystem().countActivePeers() <= 0)
|
if (_context.commSystem().countActivePeers() <= 0)
|
||||||
return;
|
return;
|
||||||
if (delta > 0)
|
if (delta > 0)
|
||||||
|
@ -10,7 +10,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.app.ClientAppManager;
|
import net.i2p.app.ClientAppManager;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.RoutingKeyGenerator;
|
||||||
import net.i2p.data.router.RouterInfo;
|
import net.i2p.data.router.RouterInfo;
|
||||||
|
import net.i2p.data.router.RouterKeyGenerator;
|
||||||
import net.i2p.internal.InternalClientManager;
|
import net.i2p.internal.InternalClientManager;
|
||||||
import net.i2p.router.client.ClientManagerFacadeImpl;
|
import net.i2p.router.client.ClientManagerFacadeImpl;
|
||||||
import net.i2p.router.crypto.TransientSessionKeyManager;
|
import net.i2p.router.crypto.TransientSessionKeyManager;
|
||||||
@ -65,6 +67,7 @@ public class RouterContext extends I2PAppContext {
|
|||||||
//private MessageStateMonitor _messageStateMonitor;
|
//private MessageStateMonitor _messageStateMonitor;
|
||||||
private RouterThrottle _throttle;
|
private RouterThrottle _throttle;
|
||||||
private RouterAppManager _appManager;
|
private RouterAppManager _appManager;
|
||||||
|
private RouterKeyGenerator _routingKeyGenerator;
|
||||||
private final Set<Runnable> _finalShutdownTasks;
|
private final Set<Runnable> _finalShutdownTasks;
|
||||||
// split up big lock on this to avoid deadlocks
|
// split up big lock on this to avoid deadlocks
|
||||||
private volatile boolean _initialized;
|
private volatile boolean _initialized;
|
||||||
@ -183,6 +186,7 @@ public class RouterContext extends I2PAppContext {
|
|||||||
_messageHistory = new MessageHistory(this);
|
_messageHistory = new MessageHistory(this);
|
||||||
_messageRegistry = new OutboundMessageRegistry(this);
|
_messageRegistry = new OutboundMessageRegistry(this);
|
||||||
//_messageStateMonitor = new MessageStateMonitor(this);
|
//_messageStateMonitor = new MessageStateMonitor(this);
|
||||||
|
_routingKeyGenerator = new RouterKeyGenerator(this);
|
||||||
if (!getBooleanProperty("i2p.dummyNetDb"))
|
if (!getBooleanProperty("i2p.dummyNetDb"))
|
||||||
_netDb = new FloodfillNetworkDatabaseFacade(this); // new KademliaNetworkDatabaseFacade(this);
|
_netDb = new FloodfillNetworkDatabaseFacade(this); // new KademliaNetworkDatabaseFacade(this);
|
||||||
else
|
else
|
||||||
@ -582,4 +586,35 @@ public class RouterContext extends I2PAppContext {
|
|||||||
_sessionKeyManagerInitialized = true;
|
_sessionKeyManagerInitialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine how much do we want to mess with the keys to turn them
|
||||||
|
* into something we can route. This is context specific because we
|
||||||
|
* may want to test out how things react when peers don't agree on
|
||||||
|
* how to skew.
|
||||||
|
*
|
||||||
|
* Returns same thing as routerKeyGenerator()
|
||||||
|
*
|
||||||
|
* @return non-null
|
||||||
|
* @since 0.9.16 Overrides I2PAppContext. Returns non-null in RouterContext and null in I2PAppcontext.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public RoutingKeyGenerator routingKeyGenerator() {
|
||||||
|
return _routingKeyGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine how much do we want to mess with the keys to turn them
|
||||||
|
* into something we can route. This is context specific because we
|
||||||
|
* may want to test out how things react when peers don't agree on
|
||||||
|
* how to skew.
|
||||||
|
*
|
||||||
|
* Returns same thing as routingKeyGenerator()
|
||||||
|
*
|
||||||
|
* @return non-null
|
||||||
|
* @since 0.9.16
|
||||||
|
*/
|
||||||
|
public RouterKeyGenerator routerKeyGenerator() {
|
||||||
|
return _routingKeyGenerator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import net.i2p.data.TunnelId;
|
|||||||
import net.i2p.data.i2np.DatabaseLookupMessage;
|
import net.i2p.data.i2np.DatabaseLookupMessage;
|
||||||
import net.i2p.data.i2np.DatabaseStoreMessage;
|
import net.i2p.data.i2np.DatabaseStoreMessage;
|
||||||
import net.i2p.data.router.RouterInfo;
|
import net.i2p.data.router.RouterInfo;
|
||||||
|
import net.i2p.data.router.RouterKeyGenerator;
|
||||||
import net.i2p.router.Job;
|
import net.i2p.router.Job;
|
||||||
import net.i2p.router.JobImpl;
|
import net.i2p.router.JobImpl;
|
||||||
import net.i2p.router.OutNetMessage;
|
import net.i2p.router.OutNetMessage;
|
||||||
@ -176,16 +177,17 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
|
|||||||
*/
|
*/
|
||||||
public void flood(DatabaseEntry ds) {
|
public void flood(DatabaseEntry ds) {
|
||||||
Hash key = ds.getHash();
|
Hash key = ds.getHash();
|
||||||
Hash rkey = _context.routingKeyGenerator().getRoutingKey(key);
|
RouterKeyGenerator gen = _context.routerKeyGenerator();
|
||||||
|
Hash rkey = gen.getRoutingKey(key);
|
||||||
FloodfillPeerSelector sel = (FloodfillPeerSelector)getPeerSelector();
|
FloodfillPeerSelector sel = (FloodfillPeerSelector)getPeerSelector();
|
||||||
List<Hash> peers = sel.selectFloodfillParticipants(rkey, MAX_TO_FLOOD, getKBuckets());
|
List<Hash> peers = sel.selectFloodfillParticipants(rkey, MAX_TO_FLOOD, getKBuckets());
|
||||||
// todo key cert skip?
|
// todo key cert skip?
|
||||||
long until = _context.routingKeyGenerator().getTimeTillMidnight();
|
long until = gen.getTimeTillMidnight();
|
||||||
if (until < NEXT_RKEY_LS_ADVANCE_TIME ||
|
if (until < NEXT_RKEY_LS_ADVANCE_TIME ||
|
||||||
(ds.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO && until < NEXT_RKEY_RI_ADVANCE_TIME)) {
|
(ds.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO && until < NEXT_RKEY_RI_ADVANCE_TIME)) {
|
||||||
// to avoid lookup failures after midnight, also flood to some closest to the
|
// to avoid lookup faulures after midnight, also flood to some closest to the
|
||||||
// next routing key for a period of time before midnight.
|
// next routing key for a period of time before midnight.
|
||||||
Hash nkey = _context.routingKeyGenerator().getNextRoutingKey(key);
|
Hash nkey = gen.getNextRoutingKey(key);
|
||||||
List<Hash> nextPeers = sel.selectFloodfillParticipants(nkey, NEXT_FLOOD_QTY, getKBuckets());
|
List<Hash> nextPeers = sel.selectFloodfillParticipants(nkey, NEXT_FLOOD_QTY, getKBuckets());
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Hash h : nextPeers) {
|
for (Hash h : nextPeers) {
|
||||||
|
@ -8,7 +8,7 @@ package net.i2p.router.tasks;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import net.i2p.data.RoutingKeyGenerator;
|
import net.i2p.data.router.RouterKeyGenerator;
|
||||||
import net.i2p.router.JobImpl;
|
import net.i2p.router.JobImpl;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
@ -33,7 +33,7 @@ public class UpdateRoutingKeyModifierJob extends JobImpl {
|
|||||||
public String getName() { return "Update Routing Key Modifier"; }
|
public String getName() { return "Update Routing Key Modifier"; }
|
||||||
|
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
RoutingKeyGenerator gen = getContext().routingKeyGenerator();
|
RouterKeyGenerator gen = getContext().routerKeyGenerator();
|
||||||
// make sure we requeue quickly if just before midnight
|
// make sure we requeue quickly if just before midnight
|
||||||
long delay = Math.max(5, Math.min(MAX_DELAY_FAILSAFE, gen.getTimeTillMidnight()));
|
long delay = Math.max(5, Math.min(MAX_DELAY_FAILSAFE, gen.getTimeTillMidnight()));
|
||||||
// TODO tell netdb if mod data changed?
|
// TODO tell netdb if mod data changed?
|
||||||
|
Reference in New Issue
Block a user