forked from I2P_Developers/i2p.i2p
NetDB: Cache blinding data for lookups and decryption (proposal #123)
This commit is contained in:
@ -1247,7 +1247,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
|
||||
} else if(ahelperPresent) {
|
||||
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
||||
} else if(destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
|
||||
} else if(destination.length() >= 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
|
||||
header = getErrorPage("nols", ERR_DESTINATION_UNKNOWN);
|
||||
extraMessage = _t("Destination lease set not found");
|
||||
} else {
|
||||
|
@ -25,6 +25,7 @@ public class BlindData {
|
||||
private long _routingKeyGenMod;
|
||||
|
||||
/**
|
||||
* @param secret may be null or zero-length
|
||||
* @throws IllegalArgumentException on various errors
|
||||
*/
|
||||
public BlindData(I2PAppContext ctx, Destination dest, SigType blindType, String secret) {
|
||||
@ -33,6 +34,7 @@ public class BlindData {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param secret may be null or zero-length
|
||||
* @throws IllegalArgumentException on various errors
|
||||
*/
|
||||
public BlindData(I2PAppContext ctx, SigningPublicKey spk, SigType blindType, String secret) {
|
||||
@ -47,13 +49,27 @@ public class BlindData {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The blinded key for the current day
|
||||
* @return The unblinded SPK, non-null
|
||||
*/
|
||||
public SigningPublicKey getUnblindedPubKey() {
|
||||
return _clearSPK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The blinded key for the current day, non-null
|
||||
*/
|
||||
public synchronized SigningPublicKey getBlindedPubKey() {
|
||||
calculate();
|
||||
return _blindSPK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The hash of the destination if known, or null
|
||||
*/
|
||||
public synchronized Hash getDestHash() {
|
||||
return _dest != null ? _dest.getHash() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The hash of the blinded key for the current day
|
||||
*/
|
||||
@ -127,4 +143,22 @@ public class BlindData {
|
||||
System.arraycopy(_blindSPK.getData(), 0, hashData, 2, _blindSPK.length());
|
||||
_blindHash = _context.sha().calculateHash(hashData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
calculate();
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
buf.append("[BlindData: ");
|
||||
buf.append("\n\tSigningPublicKey: ").append(_clearSPK);
|
||||
buf.append("\n\tAlpha : ").append(_alpha);
|
||||
buf.append("\n\tBlindedPublicKey: ").append(_blindSPK);
|
||||
buf.append("\n\tBlinded Hash : ").append(_blindHash);
|
||||
buf.append("\n\tSecret: ").append(_secret);
|
||||
if (_dest != null)
|
||||
buf.append("\n\tDestination: ").append(_dest);
|
||||
if (_dest != null)
|
||||
buf.append("\n\tDestination: unknown");
|
||||
buf.append(']');
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
private LeaseSet2 _decryptedLS2;
|
||||
private Hash __calculatedHash;
|
||||
private SigningPrivateKey _alpha;
|
||||
// to decrypt with if we don't have full dest
|
||||
private SigningPublicKey _unblindedSPK;
|
||||
private String _secret;
|
||||
private final Log _log;
|
||||
|
||||
@ -120,11 +122,31 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
_destination = dest;
|
||||
}
|
||||
SigningPublicKey spk = dest.getSigningPublicKey();
|
||||
setSigningKey(spk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to set the blinded key
|
||||
*
|
||||
* @param spk unblinded key non-null, must be EdDSA_SHA512_Ed25519 or RedDSA_SHA512_Ed25519
|
||||
* @throws IllegalStateException if already signed
|
||||
* @throws IllegalArgumentException if not EdDSA
|
||||
* @since 0.9.40
|
||||
*/
|
||||
@Override
|
||||
public void setSigningKey(SigningPublicKey spk) {
|
||||
// TODO already-set checks
|
||||
SigType type = spk.getType();
|
||||
if (type != SigType.EdDSA_SHA512_Ed25519 &&
|
||||
type != SigType.RedDSA_SHA512_Ed25519)
|
||||
throw new IllegalArgumentException();
|
||||
SigningPublicKey bpk = blind();
|
||||
if (_unblindedSPK != null) {
|
||||
if (!_unblindedSPK.equals(spk))
|
||||
throw new IllegalArgumentException("unblinded pubkey mismatch");
|
||||
} else {
|
||||
_unblindedSPK = spk;
|
||||
}
|
||||
SigningPublicKey bpk = blind(spk);
|
||||
if (_signingKey == null)
|
||||
_signingKey = bpk;
|
||||
else if (!_signingKey.equals(bpk))
|
||||
@ -139,13 +161,12 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
*
|
||||
* @since 0.9.39
|
||||
*/
|
||||
private SigningPublicKey blind() {
|
||||
SigningPublicKey spk = _destination.getSigningPublicKey();
|
||||
private SigningPublicKey blind(SigningPublicKey spk) {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
if (_published <= 0)
|
||||
_alpha = Blinding.generateAlpha(ctx, _destination.getSigningPublicKey(), _secret);
|
||||
_alpha = Blinding.generateAlpha(ctx, spk, _secret);
|
||||
else
|
||||
_alpha = Blinding.generateAlpha(ctx, _destination.getSigningPublicKey(), _secret, _published);
|
||||
_alpha = Blinding.generateAlpha(ctx, spk, _secret, _published);
|
||||
SigningPublicKey rv = Blinding.blind(spk, _alpha);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Blind:" +
|
||||
@ -476,14 +497,13 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
* @since 0.9.39
|
||||
*/
|
||||
private byte[] getSubcredential(I2PAppContext ctx) {
|
||||
if (_destination == null)
|
||||
throw new IllegalStateException("no known destination to decrypt with");
|
||||
SigningPublicKey destspk = _destination.getSigningPublicKey();
|
||||
int spklen = destspk.length();
|
||||
if (_unblindedSPK == null)
|
||||
throw new IllegalStateException("no known SPK to decrypt with");
|
||||
int spklen = _unblindedSPK.length();
|
||||
byte[] in = new byte[spklen + 4];
|
||||
// SHA256("credential" || spk || sigtypein || sigtypeout)
|
||||
System.arraycopy(destspk.getData(), 0, in, 0, spklen);
|
||||
DataHelper.toLong(in, spklen, 2, destspk.getType().getCode());
|
||||
System.arraycopy(_unblindedSPK.getData(), 0, in, 0, spklen);
|
||||
DataHelper.toLong(in, spklen, 2, _unblindedSPK.getType().getCode());
|
||||
DataHelper.toLong(in, spklen + 2, 2, SigType.RedDSA_SHA512_Ed25519.getCode());
|
||||
byte[] credential = hash(ctx, CREDENTIAL, in);
|
||||
byte[] spk = _signingKey.getData();
|
||||
@ -572,8 +592,9 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
return false;
|
||||
}
|
||||
_log.info("ELS2 outer sig verify success");
|
||||
if (_destination == null) {
|
||||
_log.warn("ELS2 no dest to decrypt with");
|
||||
if (_unblindedSPK == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("ELS2 no dest/SPK to decrypt with", new Exception("I did it"));
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
|
10
history.txt
10
history.txt
@ -1,3 +1,13 @@
|
||||
2019-03-27 zzz
|
||||
* NetDB: Cache blinding data for lookups and decryption (proposal #123)
|
||||
|
||||
2019-03-23 zzz
|
||||
* Data: Preliminary work on new b32 format (proposal #149)
|
||||
* SelfSignedGenerator:
|
||||
- Fix generation with Ed25519ph keys (ticket #2465)
|
||||
- Increase serial number from 63 to 71 bits
|
||||
* SusiDNS: Add import feature (ticket #2447)
|
||||
|
||||
2019-03-22 zzz
|
||||
* i2ptunnel: Escape {} in URLs (ticket #2130)
|
||||
|
||||
|
@ -13,10 +13,12 @@ import java.io.Writer;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.BlindData;
|
||||
import net.i2p.data.DatabaseEntry;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.networkdb.reseed.ReseedChecker;
|
||||
|
||||
@ -161,4 +163,19 @@ public abstract class NetworkDatabaseFacade implements Service {
|
||||
* @since 0.9.16
|
||||
*/
|
||||
public boolean isNegativeCachedForever(Hash key) { return false; }
|
||||
|
||||
/**
|
||||
* @param spk unblinded key
|
||||
* @return BlindData or null
|
||||
* @since 0.9.40
|
||||
*/
|
||||
public BlindData getBlindData(SigningPublicKey spk) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bd new BlindData to put in the cache
|
||||
* @since 0.9.40
|
||||
*/
|
||||
public void setBlindData(BlindData bd) {}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 1;
|
||||
public final static long BUILD = 2;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
@ -9,9 +9,12 @@ import java.util.Locale;
|
||||
import net.i2p.crypto.Blinding;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.BlindData;
|
||||
import net.i2p.data.DatabaseEntry;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.EncryptedLeaseSet;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.data.i2cp.DestReplyMessage;
|
||||
import net.i2p.data.i2cp.HostReplyMessage;
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
@ -34,6 +37,7 @@ class LookupDestJob extends JobImpl {
|
||||
private final String _name;
|
||||
private final SessionId _sessID;
|
||||
private final Hash _fromLocalDest;
|
||||
private final BlindData _blindData;
|
||||
|
||||
private static final long DEFAULT_TIMEOUT = 15*1000;
|
||||
|
||||
@ -69,6 +73,7 @@ class LookupDestJob extends JobImpl {
|
||||
_timeout = timeout;
|
||||
_sessID = sessID;
|
||||
_fromLocalDest = fromLocalDest;
|
||||
BlindData bd = null;
|
||||
if (name != null && name.length() >= 60) {
|
||||
// convert a b32 lookup to a hash lookup
|
||||
String nlc = name.toLowerCase(Locale.US);
|
||||
@ -82,8 +87,16 @@ class LookupDestJob extends JobImpl {
|
||||
name = null;
|
||||
} else if (b.length >= 35) {
|
||||
// encrypted LS2
|
||||
// lookup the blinded hash
|
||||
try {
|
||||
BlindData bd = Blinding.decode(context, b);
|
||||
bd = Blinding.decode(context, b);
|
||||
SigningPublicKey spk = bd.getUnblindedPubKey();
|
||||
BlindData bd2 = getContext().netDb().getBlindData(spk);
|
||||
if (bd2 != null) {
|
||||
bd = bd2;
|
||||
} else {
|
||||
getContext().netDb().setBlindData(bd);
|
||||
}
|
||||
h = bd.getBlindedHash();
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Converting name lookup " + name + " to blinded " + h);
|
||||
@ -99,6 +112,7 @@ class LookupDestJob extends JobImpl {
|
||||
}
|
||||
_hash = h;
|
||||
_name = name;
|
||||
_blindData = bd;
|
||||
}
|
||||
|
||||
public String getName() { return _name != null ?
|
||||
@ -107,6 +121,15 @@ class LookupDestJob extends JobImpl {
|
||||
}
|
||||
|
||||
public void runJob() {
|
||||
if (_blindData != null) {
|
||||
Destination d = _blindData.getDestination();
|
||||
if (d != null) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Found cached b33 lookup " + _name + " to " + d);
|
||||
returnDest(d);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_name != null) {
|
||||
// inline, ignore timeout
|
||||
Destination d = getContext().namingService().lookup(_name);
|
||||
@ -121,6 +144,7 @@ class LookupDestJob extends JobImpl {
|
||||
}
|
||||
} else {
|
||||
DoneJob done = new DoneJob(getContext());
|
||||
// TODO tell router this is an encrypted lookup, skip 38 or earlier ffs?
|
||||
getContext().netDb().lookupDestination(_hash, done, _timeout, _fromLocalDest);
|
||||
}
|
||||
}
|
||||
@ -132,6 +156,18 @@ class LookupDestJob extends JobImpl {
|
||||
public String getName() { return "LeaseSet Lookup Reply to Client"; }
|
||||
public void runJob() {
|
||||
Destination dest = getContext().netDb().lookupDestinationLocally(_hash);
|
||||
if (dest == null && _blindData != null) {
|
||||
// TODO store and lookup original hash instead
|
||||
LeaseSet ls = getContext().netDb().lookupLeaseSetLocally(_hash);
|
||||
if (ls != null && ls.getType() == DatabaseEntry.KEY_TYPE_ENCRYPTED_LS2) {
|
||||
// already decrypted
|
||||
EncryptedLeaseSet encls = (EncryptedLeaseSet) ls;
|
||||
LeaseSet decls = encls.getDecryptedLeaseSet();
|
||||
if (decls != null) {
|
||||
dest = decls.getDestination();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dest != null) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Found hash lookup " + _hash + " to " + dest);
|
||||
|
@ -0,0 +1,218 @@
|
||||
package net.i2p.router.networkdb.kademlia;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.crypto.Blinding;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.BlindData;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
/**
|
||||
* Cache of blinding data. See proposal 123.
|
||||
*
|
||||
* @since 0.9.40
|
||||
*/
|
||||
class BlindCache {
|
||||
|
||||
private final RouterContext _context;
|
||||
// unblinded key
|
||||
private final ConcurrentHashMap<SigningPublicKey, BlindData> _cache;
|
||||
// blinded key
|
||||
private final ConcurrentHashMap<SigningPublicKey, BlindData> _reverseCache;
|
||||
// dest hash
|
||||
private final ConcurrentHashMap<Hash, BlindData> _hashCache;
|
||||
|
||||
/**
|
||||
* Caller MUST call startup() to load persistent cache from disk
|
||||
*/
|
||||
public BlindCache(RouterContext ctx) {
|
||||
_context = ctx;
|
||||
_cache = new ConcurrentHashMap<SigningPublicKey, BlindData>(32);
|
||||
_reverseCache = new ConcurrentHashMap<SigningPublicKey, BlindData>(32);
|
||||
_hashCache = new ConcurrentHashMap<Hash, BlindData>(32);
|
||||
}
|
||||
|
||||
/**
|
||||
* May be restarted by calling startup() again.
|
||||
*/
|
||||
public synchronized void shutdown() {
|
||||
store();
|
||||
_cache.clear();
|
||||
_reverseCache.clear();
|
||||
_hashCache.clear();
|
||||
}
|
||||
|
||||
public synchronized void startup() {
|
||||
load();
|
||||
}
|
||||
|
||||
/**
|
||||
* The hash to lookup for a dest.
|
||||
* If known to be blinded, returns the current blinded hash.
|
||||
* If not known to be blinded, returns the standard dest hash.
|
||||
*
|
||||
* @param dest may or may not be blinded
|
||||
* @return the unblinded or blinded hash
|
||||
*/
|
||||
public Hash getHash(Destination dest) {
|
||||
Hash rv = getBlindedHash(dest);
|
||||
if (rv != null)
|
||||
return rv;
|
||||
return dest.getHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* The hash to lookup for a dest hash.
|
||||
* If known to be blinded, returns the current blinded hash.
|
||||
* If not known to be blinded, returns h.
|
||||
*
|
||||
* @param dest may or may not be blinded
|
||||
* @return the blinded hash or h
|
||||
*/
|
||||
public Hash getHash(Hash h) {
|
||||
BlindData bd = _hashCache.get(h);
|
||||
if (bd != null)
|
||||
return bd.getBlindedHash();
|
||||
return h;
|
||||
}
|
||||
|
||||
/**
|
||||
* The hash to lookup for a dest.
|
||||
* If known to be blinded, returns the current blinded hash.
|
||||
* If not known to be blinded, returns null.
|
||||
*
|
||||
* @param dest may or may not be blinded
|
||||
* @return the blinded hash or null if not blinded
|
||||
*/
|
||||
public Hash getBlindedHash(Destination dest) {
|
||||
BlindData bd = _cache.get(dest.getSigningPublicKey());
|
||||
if (bd != null)
|
||||
return bd.getBlindedHash();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The hash to lookup for a SPK known to be blinded.
|
||||
* Default blinded type assumed.
|
||||
* Secret assumed null.
|
||||
*
|
||||
* @param spk known to be blinded
|
||||
* @return the blinded hash
|
||||
* @throws IllegalArgumentException on various errors
|
||||
*/
|
||||
public Hash getBlindedHash(SigningPublicKey spk) {
|
||||
BlindData bd = _cache.get(spk);
|
||||
if (bd == null)
|
||||
bd = new BlindData(_context, spk, Blinding.getDefaultBlindedType(spk.getType()), null);
|
||||
addToCache(bd);
|
||||
return bd.getBlindedHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a destination as known to be blinded
|
||||
*
|
||||
* @param dest known to be blinded
|
||||
* @param blindedType null for default
|
||||
* @param secret may be null
|
||||
* @throws IllegalArgumentException on various errors
|
||||
*/
|
||||
public void setBlinded(Destination dest, SigType blindedType, String secret) {
|
||||
SigningPublicKey spk = dest.getSigningPublicKey();
|
||||
BlindData bd = _cache.get(spk);
|
||||
if (bd != null) {
|
||||
bd.setDestination(dest);
|
||||
} else {
|
||||
if (blindedType == null)
|
||||
blindedType = Blinding.getDefaultBlindedType(spk.getType());
|
||||
bd = new BlindData(_context, dest, blindedType, secret);
|
||||
bd.setDestination(dest);
|
||||
addToCache(bd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the destination to the cache entry.
|
||||
* Must previously be in cache.
|
||||
*
|
||||
* @param dest known to be blinded
|
||||
* @throws IllegalArgumentException on various errors
|
||||
*/
|
||||
public void setBlinded(Destination dest) {
|
||||
SigningPublicKey spk = dest.getSigningPublicKey();
|
||||
BlindData bd = _cache.get(spk);
|
||||
if (bd != null) {
|
||||
bd.setDestination(dest);
|
||||
_hashCache.putIfAbsent(dest.getHash(), bd);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToCache(BlindData bd) {
|
||||
_cache.put(bd.getUnblindedPubKey(), bd);
|
||||
_reverseCache.put(bd.getBlindedPubKey(), bd);
|
||||
Destination dest = bd.getDestination();
|
||||
if (dest != null)
|
||||
_hashCache.put(dest.getHash(), bd);
|
||||
}
|
||||
|
||||
/**
|
||||
* The cached data or null
|
||||
*/
|
||||
public BlindData getData(Destination dest) {
|
||||
BlindData rv = getData(dest.getSigningPublicKey());
|
||||
if (rv != null) {
|
||||
Destination d = rv.getDestination();
|
||||
if (d == null)
|
||||
rv.setDestination(dest);
|
||||
else if (!dest.equals(d))
|
||||
rv = null; // mismatch ???
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cached data or null
|
||||
*
|
||||
* @param spk the unblinded public key
|
||||
*/
|
||||
public BlindData getData(SigningPublicKey spk) {
|
||||
SigType type = spk.getType();
|
||||
if (type != SigType.EdDSA_SHA512_Ed25519 &&
|
||||
type != SigType.RedDSA_SHA512_Ed25519)
|
||||
return null;
|
||||
return _cache.get(spk);
|
||||
}
|
||||
|
||||
/**
|
||||
* The cached data or null
|
||||
*
|
||||
* @param spk the blinded public key
|
||||
*/
|
||||
public BlindData getReverseData(SigningPublicKey spk) {
|
||||
SigType type = spk.getType();
|
||||
if (type != SigType.RedDSA_SHA512_Ed25519)
|
||||
return null;
|
||||
return _reverseCache.get(spk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh all the data at midnight
|
||||
*
|
||||
*/
|
||||
public void rollover() {
|
||||
_reverseCache.clear();
|
||||
for (BlindData bd : _cache.values()) {
|
||||
_reverseCache.put(bd.getBlindedPubKey(), bd);
|
||||
}
|
||||
}
|
||||
|
||||
private void load() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void store() {
|
||||
// TODO
|
||||
}
|
||||
}
|
@ -21,15 +21,18 @@ import java.util.Set;
|
||||
|
||||
import net.i2p.crypto.SigAlgo;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.BlindData;
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.DatabaseEntry;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.EncryptedLeaseSet;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.KeyCertificate;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.LeaseSet2;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.data.i2np.DatabaseLookupMessage;
|
||||
import net.i2p.data.i2np.DatabaseStoreMessage;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
@ -73,6 +76,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
private volatile long _lastRIPublishTime;
|
||||
private NegativeLookupCache _negativeCache;
|
||||
protected final int _networkID;
|
||||
private final BlindCache _blindCache;
|
||||
|
||||
/**
|
||||
* Map of Hash to RepublishLeaseSetJob for leases we'realready managing.
|
||||
@ -171,6 +175,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
_publishingLeaseSets = new HashMap<Hash, RepublishLeaseSetJob>(8);
|
||||
_activeRequests = new HashMap<Hash, SearchJob>(8);
|
||||
_reseedChecker = new ReseedChecker(context);
|
||||
_blindCache = new BlindCache(context);
|
||||
context.statManager().createRateStat("netDb.lookupDeferred", "how many lookups are deferred?", "NetworkDatabase", new long[] { 60*60*1000 });
|
||||
context.statManager().createRateStat("netDb.exploreKeySet", "how many keys are queued for exploration?", "NetworkDatabase", new long[] { 60*60*1000 });
|
||||
context.statManager().createRateStat("netDb.negativeCache", "Aborted lookup, already cached", "NetworkDatabase", new long[] { 60*60*1000l });
|
||||
@ -246,7 +251,9 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
//_ds = null;
|
||||
_exploreKeys.clear(); // hope this doesn't cause an explosion, it shouldn't.
|
||||
// _exploreKeys = null;
|
||||
if (_negativeCache != null)
|
||||
_negativeCache.clear();
|
||||
_blindCache.shutdown();
|
||||
}
|
||||
|
||||
public synchronized void restart() {
|
||||
@ -257,6 +264,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
}
|
||||
_ds.restart();
|
||||
_exploreKeys.clear();
|
||||
_blindCache.startup();
|
||||
|
||||
_initialized = true;
|
||||
|
||||
@ -287,6 +295,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
// _exploreKeys = new HashSet(64);
|
||||
_dbDir = dbDir;
|
||||
_negativeCache = new NegativeLookupCache(_context);
|
||||
_blindCache.startup();
|
||||
|
||||
createHandlers();
|
||||
|
||||
@ -463,6 +472,27 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
return _kb.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param spk unblinded key
|
||||
* @return BlindData or null
|
||||
* @since 0.9.40
|
||||
*/
|
||||
@Override
|
||||
public BlindData getBlindData(SigningPublicKey spk) {
|
||||
return _blindCache.getData(spk);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bd new BlindData to put in the cache
|
||||
* @since 0.9.40
|
||||
*/
|
||||
@Override
|
||||
public void setBlindData(BlindData bd) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Adding to blind cache: " + bd);
|
||||
_blindCache.addToCache(bd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RouterInfo, LeaseSet, or null, validated
|
||||
* @since 0.8.3
|
||||
@ -476,10 +506,12 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
int type = rv.getType();
|
||||
if (DatabaseEntry.isLeaseSet(type)) {
|
||||
LeaseSet ls = (LeaseSet)rv;
|
||||
if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR))
|
||||
if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) {
|
||||
return rv;
|
||||
else
|
||||
} else {
|
||||
key = _blindCache.getHash(key);
|
||||
fail(key);
|
||||
}
|
||||
} else if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
|
||||
try {
|
||||
if (validate((RouterInfo)rv) == null)
|
||||
@ -533,6 +565,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
} else {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("leaseSet not found locally, running search");
|
||||
key = _blindCache.getHash(key);
|
||||
search(key, onFindJob, onFailedLookupJob, timeoutMs, true, fromLocalDest);
|
||||
}
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
@ -549,6 +582,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
*/
|
||||
public void lookupLeaseSetRemotely(Hash key, Hash fromLocalDest) {
|
||||
if (!_initialized) return;
|
||||
key = _blindCache.getHash(key);
|
||||
search(key, null, null, 20*1000, true, fromLocalDest);
|
||||
}
|
||||
|
||||
@ -564,6 +598,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) {
|
||||
return ls;
|
||||
} else {
|
||||
key = _blindCache.getHash(key);
|
||||
fail(key);
|
||||
// this was an interesting key, so either refetch it or simply explore with it
|
||||
_exploreKeys.add(key);
|
||||
@ -599,6 +634,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
_log.info("Negative cached, not searching dest: " + key);
|
||||
_context.jobQueue().addJob(onFinishedJob);
|
||||
} else {
|
||||
key = _blindCache.getHash(key);
|
||||
search(key, onFinishedJob, onFinishedJob, timeoutMs, true, fromLocalDest);
|
||||
}
|
||||
}
|
||||
@ -892,12 +928,47 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
if (rv != null && !leaseSet.getDestination().equals(rv.getDestination()))
|
||||
throw new IllegalArgumentException("LS Hash collision");
|
||||
|
||||
EncryptedLeaseSet encls = null;
|
||||
if (leaseSet.getType() == DatabaseEntry.KEY_TYPE_ENCRYPTED_LS2) {
|
||||
// set dest or key before validate() calls verifySignature() which
|
||||
// will do the decryption
|
||||
BlindData bd = _blindCache.getReverseData(leaseSet.getSigningKey());
|
||||
if (bd != null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Found blind data for encls: " + bd);
|
||||
encls = (EncryptedLeaseSet) leaseSet;
|
||||
Destination dest = bd.getDestination();
|
||||
if (dest != null) {
|
||||
encls.setDestination(dest);
|
||||
} else {
|
||||
encls.setSigningKey(bd.getUnblindedPubKey());
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("No blind data found for encls: " + encls);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String err = validate(key, leaseSet);
|
||||
if (err != null)
|
||||
throw new IllegalArgumentException("Invalid store attempt - " + err);
|
||||
|
||||
_ds.put(key, leaseSet);
|
||||
|
||||
if (encls != null) {
|
||||
// we now have decrypted it, store it as well
|
||||
LeaseSet decls = encls.getDecryptedLeaseSet();
|
||||
if (decls != null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Successfully decrypted encls: " + decls);
|
||||
// recursion
|
||||
Destination dest = decls.getDestination();
|
||||
store(dest.getHash(), decls);
|
||||
_blindCache.setBlinded(dest);
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through the old failure / success count, copying over the old
|
||||
// values (if any tunnels overlap between leaseSets). no need to be
|
||||
// ueberthreadsafe fascists here, since these values are just heuristics
|
||||
|
Reference in New Issue
Block a user