propagate from branch 'i2p.i2p.zab.2464' (head 47c50de0eaf4a41d0c0b2df3505ff3b885163791)

to branch 'i2p.i2p' (head a34db176d9f6313db1b8fd16926c8c2ca7e12e09)
This commit is contained in:
zab2
2019-04-02 17:32:39 +00:00
23 changed files with 807 additions and 73 deletions

View File

@ -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 {

View File

@ -171,7 +171,7 @@ class SAMUtils {
String msg;
if (s.length() >= 516)
msg = "Bad Base64 dest: ";
else if (s.length() == 60 && s.endsWith(".b32.i2p"))
else if (s.length() >= 60 && s.endsWith(".b32.i2p"))
msg = "Lease set not found: ";
else
msg = "Host name not found: ";

View File

@ -218,6 +218,7 @@ public final class Blinding {
* PRELIMINARY - Subject to change - see proposal 149
*
* @param b 35+ bytes
* @return BlindData structure, use getUnblindedPubKey() for the result
* @throws IllegalArgumentException on bad inputs
* @throws UnsupportedOperationException unless supported SigTypes
* @since 0.9.40
@ -313,6 +314,15 @@ public final class Blinding {
return Base32.encode(b) + ".b32.i2p";
}
public static void main(String args[]) throws Exception {
if (args.length != 1) {
System.out.println("Usage: blinding {56 chars}.b32.i2p");
System.exit(1);
}
System.out.println("Blinded B32: " + args[0]);
System.out.println(decode(I2PAppContext.getGlobalContext(), args[0]).toString());
}
/******
public static void main(String args[]) throws Exception {
net.i2p.data.SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(TYPE);

View File

@ -249,7 +249,8 @@ public final class SelfSignedGenerator {
cert.verify(cpub);
if (!cpub.equals(jpub)) {
boolean ok = false;
if (cpub.getClass().getName().equals("sun.security.x509.X509Key")) {
if ((jpub instanceof EdDSAPublicKey) &&
cpub.getClass().getName().equals("sun.security.x509.X509Key")) {
// X509Certificate will sometimes contain an X509Key rather than the EdDSAPublicKey itself; the contained
// key is valid but needs to be instanced as an EdDSAPublicKey before it can be used.
try {

View File

@ -0,0 +1,69 @@
package net.i2p.crypto;
import java.security.spec.AlgorithmParameterSpec;
import net.i2p.data.DataHelper;
/**
* Defines the context for signing with personalized hashes.
* See proposal 148.
*
* @since 0.9.40
*/
public enum SigContext {
SC_NONE (null),
SC_DATAGRAM ("sign_datagramI2P"),
SC_I2CP ("I2CP_SessionConf"),
SC_NETDB ("network_database"),
SC_NTCP ("NTCP_1_handshake"),
SC_SSU ("SSUHandshakeSign"),
SC_STREAMING("streaming_i2psig"),
SC_SU3 ("i2pSU3FileFormat"),
SC_TEST ("test1234test5678"),
;
private final SigContextSpec spec;
/**
* The 16 bytes for this type, or null for none
*/
SigContext(String p) {
spec = new SigContextSpec(p);
}
/**
* The AlgorithmParameterSpec.
* Pass this as an argument in setParameter()
* to the Blake sign/verify engines.
*/
public SigContextSpec getSpec() { return spec; }
/**
* The AlgorithmParameterSpec.
* Pass this as an argument in setParameter()
* to the Blake sign/verify engines.
*/
public static class SigContextSpec implements AlgorithmParameterSpec {
private final byte[] b;
/**
* The 16 bytes for this type, or null for none
*/
public SigContextSpec(String p) {
if (p != null) {
b = DataHelper.getASCII(p);
if (b.length != 16)
throw new IllegalArgumentException();
} else {
b = null;
}
}
/**
* The 16 bytes for this type, or null for none
*/
public byte[] getData() { return b; }
}
}

View File

@ -115,7 +115,7 @@ public class Base32 {
private static void decode(InputStream in, OutputStream out) throws IOException {
byte decoded[] = decode(DataHelper.getUTF8(read(in)));
if (decoded == null) {
System.out.println("FAIL");
System.err.println("FAIL");
return;
}
out.write(decoded);

View File

@ -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) {
@ -40,20 +42,41 @@ public class BlindData {
_clearSPK = spk;
_blindType = blindType;
_secret = secret;
_authType = 0;
_authType = -1;
_authKey = null;
// defer until needed
//calculate();
}
/**
* @return The blinded key for the current day
* @return The unblinded SPK, non-null
*/
public SigningPublicKey getUnblindedPubKey() {
return _clearSPK;
}
/**
* @return The type of the blinded key
*/
public SigType getBlindedSigType() {
return _blindType;
}
/**
* @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
*/
@ -99,12 +122,19 @@ public class BlindData {
}
/**
* @return 0 for no client auth
* @return -1 for no client auth
*/
public int getAuthType() {
return _authType;
}
/**
* @return null for no client auth
*/
public PrivateKey getAuthPrivKey() {
return _authKey;
}
private synchronized void calculate() {
if (_context.isRouterContext()) {
RoutingKeyGenerator gen = _context.routingKeyGenerator();
@ -127,4 +157,30 @@ 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(1024);
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);
if (_secret != null)
buf.append("\n\tSecret : \"").append(_secret).append('"');
buf.append("\n\tAuth Type : ");
if (_authType >= 0)
buf.append(_authType);
else
buf.append("none");
if (_authKey != null)
buf.append("\n\tAuth Key : ").append(_authKey);
if (_dest != null)
buf.append("\n\tDestination: ").append(_dest);
else
buf.append("\n\tDestination: unknown");
buf.append(']');
return buf.toString();
}
}

View File

@ -59,8 +59,9 @@ public abstract class DatabaseEntry extends DataStructureImpl {
public final static int KEY_TYPE_SERVICE_LIST = 11;
protected volatile Signature _signature;
protected volatile Hash _currentRoutingKey;
protected volatile long _routingKeyGenMod;
// synch: this
private Hash _currentRoutingKey;
private long _routingKeyGenMod;
/**
* A common interface to the timestamp of the two subclasses.
@ -153,11 +154,13 @@ public abstract class DatabaseEntry extends DataStructureImpl {
throw new IllegalStateException("Not in router context");
RoutingKeyGenerator gen = ctx.routingKeyGenerator();
long mod = gen.getLastChanged();
if (mod != _routingKeyGenMod) {
_currentRoutingKey = gen.getRoutingKey(getHash());
_routingKeyGenMod = mod;
synchronized(this) {
if (mod != _routingKeyGenMod) {
_currentRoutingKey = gen.getRoutingKey(getHash());
_routingKeyGenMod = mod;
}
return _currentRoutingKey;
}
return _currentRoutingKey;
}
/**

View File

@ -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 {

View File

@ -10,9 +10,11 @@ package net.i2p.data;
*/
import java.util.Arrays;
import javax.security.auth.Destroyable;
import net.i2p.crypto.EncType;
import net.i2p.crypto.KeyGenerator;
import net.i2p.util.SimpleByteCache;
/**
* Defines the PrivateKey as defined by the I2P data structure spec.
@ -24,7 +26,7 @@ import net.i2p.crypto.KeyGenerator;
*
* @author jrandom
*/
public class PrivateKey extends SimpleDataStructure {
public class PrivateKey extends SimpleDataStructure implements Destroyable {
private static final EncType DEF_TYPE = EncType.ELGAMAL_2048;
public final static int KEYSIZE_BYTES = DEF_TYPE.getPrivkeyLen();
@ -89,13 +91,36 @@ public class PrivateKey extends SimpleDataStructure {
return KeyGenerator.getPublicKey(this);
}
/**
* javax.security.auth.Destroyable interface
*
* @since 0.9.40
*/
public void destroy() {
byte[] data = _data;
if (data != null) {
_data = null;
Arrays.fill(data, (byte) 0);
SimpleByteCache.release(data);
}
}
/**
* javax.security.auth.Destroyable interface
*
* @since 0.9.40
*/
public boolean isDestroyed() {
return _data == null;
}
/**
* @since 0.9.38
*/
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("[PrivateKey ").append(_type).append(": ");
buf.append("[PrivateKey ").append(_type).append(' ');
int length = length();
if (_data == null) {
buf.append("null");

View File

@ -10,10 +10,12 @@ package net.i2p.data;
*/
import java.util.Arrays;
import javax.security.auth.Destroyable;
import net.i2p.crypto.Blinding;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SigType;
import net.i2p.util.SimpleByteCache;
/**
* Defines the SigningPrivateKey as defined by the I2P data structure spec.
@ -26,7 +28,7 @@ import net.i2p.crypto.SigType;
*
* @author jrandom
*/
public class SigningPrivateKey extends SimpleDataStructure {
public class SigningPrivateKey extends SimpleDataStructure implements Destroyable {
private static final SigType DEF_TYPE = SigType.DSA_SHA1;
public final static int KEYSIZE_BYTES = DEF_TYPE.getPrivkeyLen();
@ -115,13 +117,36 @@ public class SigningPrivateKey extends SimpleDataStructure {
return b == 0;
}
/**
* javax.security.auth.Destroyable interface
*
* @since 0.9.40
*/
public void destroy() {
byte[] data = _data;
if (data != null) {
_data = null;
Arrays.fill(data, (byte) 0);
SimpleByteCache.release(data);
}
}
/**
* javax.security.auth.Destroyable interface
*
* @since 0.9.40
*/
public boolean isDestroyed() {
return _data == null;
}
/**
* @since 0.9.8
*/
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("[SigningPrivateKey ").append(_type).append(": ");
buf.append("[SigningPrivateKey ").append(_type).append(' ');
int length = length();
if (_data == null) {
buf.append("null");

View File

@ -9,11 +9,10 @@ Build-Depends: debhelper (>= 7.0.50~)
,ant (>= 1.8)
,debconf
,openjdk-7-jdk
# Ant requires java 6 tools.jar:
# Unable to locate tools.jar. Expected to find it in /usr/lib/jvm/java-6-openjdk-amd64/lib/tools.jar
,openjdk-6-jdk
,libtomcat8-java
,dh-apparmor
,dh-systemd
,bash-completion
,gettext
,libgetopt-java
,libjson-simple-java (<< 3)
@ -33,7 +32,7 @@ Depends: ${java:Depends}, ${shlibs:Depends},
libjbigi-jni,
lsb-base (>= 3.0-6),
service-wrapper
Description: Anonymous network (I2P)
Description: Invisible Internet Project (I2P) - anonymous network
I2P is an anonymizing network, offering a simple layer that identity-sensitive
applications can use to securely communicate. All data is wrapped with several
layers of encryption, and the network is both distributed and dynamic, with no
@ -48,7 +47,7 @@ Section: java
Priority: optional
Depends: ${shlibs:Depends}, i2p-router
Homepage: https://geti2p.net/
Description: I2P libjbigi library
Description: Invisible Internet Project (I2P) - libjbigi library
This Package contains the libjbigi JNI library (and on x86 platforms, jcpuid).
.
libjbigi is a math library that is part of the I2P installation. Use of this
@ -62,7 +61,7 @@ Section: doc
Priority: extra
Depends: ${misc:Depends}
Suggests: i2p, default-jdk-doc
Description: I2P developer documentation
Description: Invisible Internet Project (I2P) - developer documentation
I2P is an anonymizing network, offering a simple layer that identity-sensitive
applications can use to securely communicate. All data is wrapped with several
layers of encryption, and the network is both distributed and dynamic, with no
@ -88,7 +87,7 @@ Recommends: libjbigi-jni, fonts-dejavu
Suggests: apparmor
,privoxy
,syndie
Description: Router for I2P
Description: Invisible Internet Project (I2P) - Router
I2P is an anonymizing network, offering a simple layer that identity-sensitive
applications can use to securely communicate. All data is wrapped with several
layers of encryption, and the network is both distributed and dynamic, with no

View File

@ -9,11 +9,9 @@ Build-Depends: debhelper (>= 7.0.50~)
,ant (>= 1.8)
,debconf
,openjdk-7-jdk
# Ant requires java 6 tools.jar:
# Unable to locate tools.jar. Expected to find it in /usr/lib/jvm/java-6-openjdk-amd64/lib/tools.jar
,openjdk-6-jdk
,glassfish-javaee
,dh-apparmor
,dh-systemd
,bash-completion
,gettext
,libgetopt-java

View File

@ -9,9 +9,6 @@ Build-Depends: debhelper (>= 7.0.50~)
,ant (>= 1.8)
,debconf
,openjdk-7-jdk
# Ant requires java 6 tools.jar:
# Unable to locate tools.jar. Expected to find it in /usr/lib/jvm/java-6-openjdk-amd64/lib/tools.jar
,openjdk-6-jdk
,glassfish-javaee
,dh-apparmor
,dh-systemd

52
debian/po/sv.po vendored
View File

@ -11,22 +11,24 @@
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n"
"POT-Creation-Date: 2015-02-18 22:14+0000\n"
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
"Last-Translator: Jony\n"
"Language-Team: Swedish (Sweden) (http://www.transifex.com/otf/I2P/language/sv_SE/)\n"
"Report-Msgid-Bugs-To: i2p@packages.debian.org\n"
"POT-Creation-Date: 2017-11-12 14:01+0000\n"
"PO-Revision-Date: 2019-03-26 06:10+0100\n"
"Last-Translator: Jonatan Nyberg <jonatan.nyberg.karl@gmail.com>\n"
"Language-Team: Swedish (Sweden) (http://www.transifex.com/otf/I2P/language/"
"sv_SE/)\n"
"Language: sv_SE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: sv_SE\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.2.1\n"
#. Type: boolean
#. Description
#: ../i2p.templates:2001
msgid "Should the I2P router be started at boot?"
msgstr "Ska I2P routern startas vid systemstart?"
msgstr "Ska I2P-routern startas vid systemstart?"
#. Type: boolean
#. Description
@ -34,7 +36,9 @@ msgstr "Ska I2P routern startas vid systemstart?"
msgid ""
"The I2P router can be run as a daemon that starts automatically when your "
"computer boots up. This is the recommended configuration."
msgstr "I2P routern kan köras som en tjänst (demon) som automatiskt startas när datorn startas. Detta är den rekommenderade konfigurationen. "
msgstr ""
"I2P-routern kan köras som en tjänst (demon) som automatiskt startas när "
"datorn startas. Detta är den rekommenderade konfigurationen."
#. Type: string
#. Description
@ -46,11 +50,15 @@ msgstr "I2P-demon användare:"
#. Description
#: ../i2p.templates:3001
msgid ""
"By default I2P is configured to run under the account i2psvc when running as"
" a daemon. To use an **existing** I2P profile you may enter a different "
"account name here. For example, if your previous I2P installation is at "
"/home/user/i2p, you may enter 'user' here."
msgstr "Som standard är I2P inställt för att köras under användaren i2psvc när det körs som tjänst. För att använda ett **existerande** I2P profil, ange en annan användare här. Exempelvis, om din tidigare I2P installation är /home/user/i2p så ange 'user' här."
"By default I2P is configured to run under the account i2psvc when running as "
"a daemon. To use an **existing** I2P profile you may enter a different "
"account name here. For example, if your previous I2P installation is at /"
"home/user/i2p, you may enter 'user' here."
msgstr ""
"Som standard är I2P konfigurerad för att köras under användaren i2psvc när "
"det körs som tjänst. Om du vill använda en **befintlig** I2P-profil kan du "
"ange ett annat kontonamn här. Till exempel, om din tidigare I2P-installation "
"är på /home/user/I2P, kan du ange \"användare\" här."
#. Type: string
#. Description
@ -58,7 +66,9 @@ msgstr "Som standard är I2P inställt för att köras under användaren i2psvc
msgid ""
"Very important: If a user other than the default of 'i2psvc' is entered "
"here, the chosen username *MUST* already exist."
msgstr "OBS! Viktigt: Om en annan användare än standard 'i2psvc' skrivs in här. *MÅSTE* det användarnamnet redan existera."
msgstr ""
"Mycket viktigt: om en annan användare än standard 'i2psvc' anges här, "
"*MÅSTE* det valda användarnamnet *redan finnas."
#. Type: string
#. Description
@ -70,7 +80,7 @@ msgstr "Minne som kan tilldelas I2P:"
#. Description
#: ../i2p.templates:4001
msgid "By default, I2P will only be allowed to use up to 128MB of RAM."
msgstr "Som standard kommer I2P bara att använda up till 128MB RAM."
msgstr "Som standard r I2P endast använda upp till 128 MB RAM."
#. Type: string
#. Description
@ -78,13 +88,15 @@ msgstr "Som standard kommer I2P bara att använda up till 128MB RAM."
msgid ""
"High bandwidth routers, as well as routers with a lot of active torrents / "
"plugins, may need to have this value increased."
msgstr "För routrar med hög bandbredd eller routrar med hög aktivitet kan detta behöva ökas"
msgstr ""
"För routrar med hög bandbredd samt routrar med en hel del aktiva torrenter / "
"insticksmoduler, kan detta värde behöva ökas."
#. Type: boolean
#. Description
#: ../i2p.templates:5001
msgid "Run I2P daemon confined with AppArmor"
msgstr "Kör I2P begränsad av AppArmor"
msgid "Should the I2P daemon be confined with AppArmor?"
msgstr "Ska I2P-demonen vara begränsad med AppArmor?"
#. Type: boolean
#. Description
@ -92,4 +104,6 @@ msgstr "Kör I2P begränsad av AppArmor"
msgid ""
"With this option enabled I2P will be sandboxed with AppArmor, restricting "
"which files and directories may be accessed by I2P."
msgstr "Med det här valet aktiverat kommer I2P att köras i sandlåda med AppArmor, som begränsar vilka filer och mappar som kan kommas åt av I2P."
msgstr ""
"Med det här alternativet aktiverat kommer I2P att köras i sandlåda med "
"AppArmor, som begränsar vilka filer och mappar som kan kommas åt av I2P."

View File

@ -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)

View File

@ -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) {}
}

View File

@ -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 = "";

View File

@ -608,7 +608,7 @@ class ClientConnectionRunner {
* called after a new leaseSet is granted by the client, the NetworkDb has been
* updated. This takes care of all the LeaseRequestState stuff (including firing any jobs)
*
* @param ls, if encrypted, the encrypted LS, not the decrypted one
* @param ls if encrypted, the encrypted LS, not the decrypted one
*/
void leaseSetCreated(LeaseSet ls) {
Hash h = ls.getDestination().calculateHash();

View File

@ -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);
@ -91,7 +104,9 @@ class LookupDestJob extends JobImpl {
} catch (RuntimeException re) {
if (_log.shouldWarn())
_log.debug("Failed blinding conversion of " + name, re);
// lookup as a name, which will probably fail
// Do NOT lookup as a name, naming service will call us again and infinite loop
name = null;
// h and name both null, runJob will fail immediately
}
}
}
@ -99,6 +114,7 @@ class LookupDestJob extends JobImpl {
}
_hash = h;
_name = name;
_blindData = bd;
}
public String getName() { return _name != null ?
@ -107,6 +123,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);
@ -119,9 +144,13 @@ class LookupDestJob extends JobImpl {
_log.debug("Failed name lookup " + _name);
returnFail();
}
} else {
} else if (_hash != null) {
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);
} else {
// blinding decode fail
returnFail();
}
}
@ -132,6 +161,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);
@ -163,8 +204,10 @@ class LookupDestJob extends JobImpl {
I2CPMessage msg;
if (_reqID >= 0)
msg = new HostReplyMessage(_sessID, HostReplyMessage.RESULT_FAILURE, _reqID);
else
else if (_hash != null)
msg = new DestReplyMessage(_hash);
else
return; // shouldn't happen
try {
_runner.doSend(msg);
} catch (I2CPMessageException ime) {}

View File

@ -76,7 +76,7 @@ public class SendMessageDirectJob extends JobImpl {
_targetHash = toPeer;
if (timeoutMs < 10*1000) {
if (_log.shouldLog(Log.WARN))
_log.warn("Very little time given [" + timeoutMs + "], resetting to 5s", new Exception("stingy caller!"));
_log.warn("Very little time given [" + timeoutMs + "], resetting to 10s", new Exception("stingy caller!"));
_expiration = ctx.clock().now() + 10*1000;
} else {
_expiration = timeoutMs + ctx.clock().now();

View File

@ -0,0 +1,375 @@
package net.i2p.router.networkdb.kademlia;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.crypto.Blinding;
import net.i2p.crypto.EncType;
import net.i2p.crypto.SigType;
import net.i2p.data.Base64;
import net.i2p.data.BlindData;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.PrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;
/**
* 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;
private static final String PERSIST_FILE = "router.blindcache.dat";
/**
* 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 h 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 synchronized void rollover() {
_reverseCache.clear();
for (BlindData bd : _cache.values()) {
_reverseCache.put(bd.getBlindedPubKey(), bd);
}
}
/**
* Load from file.
* Format:
* sigtype,bsigtype,b64 pubkey,[b64 secret],[b64 dest]
*/
private synchronized void load() {
File file = new File(_context.getConfigDir(), PERSIST_FILE);
if (!file.exists())
return;
Log log = _context.logManager().getLog(BlindCache.class);
int count = 0;
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(
new FileInputStream(file), "ISO-8859-1"));
String line = null;
while ( (line = br.readLine()) != null) {
if (line.startsWith("#"))
continue;
try {
addToCache(fromPersistentString(line));
count++;
} catch (IllegalArgumentException iae) {
if (log.shouldLog(Log.WARN))
log.warn("Error reading cache entry", iae);
} catch (DataFormatException dfe) {
if (log.shouldLog(Log.WARN))
log.warn("Error reading cache entry", dfe);
}
}
} catch (IOException ioe) {
if (log.shouldLog(Log.WARN) && file.exists())
log.warn("Error reading the blinding cache file", ioe);
} finally {
if (br != null) try { br.close(); } catch (IOException ioe) {}
}
if (log.shouldLog(Log.INFO))
log.info("Loaded " + count + " entries from " + file);
}
private synchronized void store() {
if (_cache.isEmpty())
return;
Log log = _context.logManager().getLog(BlindCache.class);
int count = 0;
File file = new File(_context.getConfigDir(), PERSIST_FILE);
PrintWriter out = null;
try {
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "ISO-8859-1")));
out.println("# Blinding cache entries. Format is: sigtype,bsigtype,authtype,time,key,[secret],[privkey],[dest]");
for (BlindData bd : _cache.values()) {
out.println(toPersistentString(bd));
count++;
}
if (out.checkError())
throw new IOException("Failed write to " + file);
} catch (IOException ioe) {
if (log.shouldLog(Log.WARN))
log.warn("Error writing the blinding cache File", ioe);
} finally {
if (out != null) out.close();
}
if (log.shouldLog(Log.INFO))
log.info("Stored " + count + " entries to " + file);
}
/**
* Format:
* sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest]
*/
private BlindData fromPersistentString(String line) throws DataFormatException {
String[] ss = DataHelper.split(line, ",", 8);
if (ss.length != 8)
throw new DataFormatException("bad format");
int ist1, ist2, auth;
long time;
try {
ist1 = Integer.parseInt(ss[0]);
ist2 = Integer.parseInt(ss[1]);
auth = Integer.parseInt(ss[2]);
time = Long.parseLong(ss[3]);
} catch (NumberFormatException nfe) {
throw new DataFormatException("bad codes", nfe);
}
SigType st1 = SigType.getByCode(ist1);
SigType st2 = SigType.getByCode(ist2);
if (st1 == null || !st1.isAvailable() || st2 == null || !st2.isAvailable())
throw new DataFormatException("bad codes");
SigningPublicKey spk = new SigningPublicKey(st1);
spk.fromBase64(ss[4]);
String secret;
if (ss[5].length() > 0) {
byte[] b = Base64.decode(ss[5]);
if (b == null)
throw new DataFormatException("bad secret");
secret = DataHelper.getUTF8(b);
} else {
secret = null;
}
PrivateKey privkey;
if (ss[6].length() > 0) {
byte[] b = Base64.decode(ss[6]);
if (b == null)
throw new DataFormatException("bad privkey");
privkey = new PrivateKey(EncType.ECIES_X25519, b);
} else {
privkey = null;
}
BlindData rv;
// TODO pass privkey
if (ss[7].length() > 0) {
Destination dest = new Destination(ss[7]);
if (!spk.equals(dest.getSigningPublicKey()))
throw new DataFormatException("spk mismatch");
rv = new BlindData(_context, dest, st2, secret);
} else {
rv = new BlindData(_context, spk, st2, secret);
}
return rv;
}
/**
* Format:
* sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest]
*/
private static String toPersistentString(BlindData bd) {
StringBuilder buf = new StringBuilder(1024);
SigningPublicKey spk = bd.getUnblindedPubKey();
buf.append(spk.getType().getCode()).append(',');
buf.append(bd.getBlindedSigType().getCode()).append(',');
buf.append(bd.getAuthType()).append(',');
// timestamp todo
buf.append('0').append(',');
buf.append(spk.toBase64()).append(',');
String secret = bd.getSecret();
if (secret != null && secret.length() > 0)
buf.append(Base64.encode(secret));
buf.append(',');
PrivateKey pk = bd.getAuthPrivKey();
if (pk != null)
buf.append(pk.toBase64());
buf.append(',');
Destination dest = bd.getDestination();
if (dest != null)
buf.append(dest.toBase64());
return buf.toString();
}
}

View File

@ -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;
_negativeCache.clear();
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