diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
index bc0748e837..985cc749dd 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
@@ -230,6 +230,26 @@ public class EditBean extends IndexBean {
return SigType.isAvailable(code);
}
+ /**
+ * Random keys, hidden in forms
+ * @since 0.9.18
+ */
+ public String getKey1(int tunnel) {
+ return getProperty(tunnel, "inbound.randomKey", "");
+ }
+
+ public String getKey2(int tunnel) {
+ return getProperty(tunnel, "outbound.randomKey", "");
+ }
+
+ public String getKey3(int tunnel) {
+ return getProperty(tunnel, "i2cp.leaseSetSigningPrivateKey", "");
+ }
+
+ public String getKey4(int tunnel) {
+ return getProperty(tunnel, "i2cp.leaseSetPrivateKey", "");
+ }
+
/** @since 0.8.9 */
public boolean getDCC(int tunnel) {
return getBooleanProperty(tunnel, I2PTunnelIRCClient.PROP_DCC);
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
index a1a8490b2d..a3bec064ea 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
@@ -10,6 +10,7 @@ package net.i2p.i2ptunnel.web;
import java.io.File;
import java.io.IOException;
+import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -25,12 +26,15 @@ import net.i2p.I2PAppContext;
import net.i2p.app.ClientAppManager;
import net.i2p.app.Outproxy;
import net.i2p.client.I2PClient;
+import net.i2p.crypto.KeyGenerator;
+import net.i2p.crypto.SigType;
import net.i2p.data.Base64;
import net.i2p.data.Certificate;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.PrivateKeyFile;
import net.i2p.data.SessionKey;
+import net.i2p.data.SimpleDataStructure;
import net.i2p.i2ptunnel.I2PTunnelClientBase;
import net.i2p.i2ptunnel.I2PTunnelConnectClient;
import net.i2p.i2ptunnel.I2PTunnelHTTPClient;
@@ -1113,6 +1117,30 @@ public class IndexBean {
// Otherwise this only works on a new tunnel...
}
+ /**
+ * Random keys, hidden in forms
+ * @since 0.9.18
+ */
+ public void setKey1(String s) {
+ if (s != null)
+ _otherOptions.put("inbound.randomKey", s.trim());
+ }
+
+ public void setKey2(String s) {
+ if (s != null)
+ _otherOptions.put("outbound.randomKey", s.trim());
+ }
+
+ public void setKey3(String s) {
+ if (s != null)
+ _otherOptions.put("i2cp.leaseSetSigningPrivateKey", s.trim());
+ }
+
+ public void setKey4(String s) {
+ if (s != null)
+ _otherOptions.put("i2cp.leaseSetPrivateKey", s.trim());
+ }
+
/** Modify or create a destination */
private String modifyDestination() {
if (_privKeyFile == null || _privKeyFile.trim().length() <= 0)
@@ -1332,11 +1360,10 @@ public class IndexBean {
}
}
- // As of 0.9.17, add a persistent random key if not present
if (!isClient(_type) || _booleanOptions.contains("persistentClientKey")) {
+ // As of 0.9.17, add a persistent random key if not present
String p = OPT + "inbound.randomKey";
if (!config.containsKey(p)) {
- // as of 0.9.17, add a random key if not previously present
byte[] rk = new byte[32];
_context.random().nextBytes(rk);
config.setProperty(p, Base64.encode(rk));
@@ -1344,6 +1371,22 @@ public class IndexBean {
_context.random().nextBytes(rk);
config.setProperty(p, Base64.encode(rk));
}
+ // As of 0.9.18, add persistent leaseset keys if not present
+ // but only if we know the sigtype
+ p = OPT + "i2cp.leaseSetSigningPrivateKey";
+ Destination dest = getDestination(_tunnel);
+ if (dest != null && !config.containsKey(p)) {
+ try {
+ SigType type = dest.getSigType();
+ SimpleDataStructure keys[] = KeyGenerator.getInstance().generateSigningKeys(type);
+ config.setProperty(p, type.name() + ':' + keys[1].toBase64());
+ p = OPT + "i2cp.leaseSetPrivateKey";
+ keys = KeyGenerator.getInstance().generatePKIKeys();
+ config.setProperty(p, "ELGAMAL_2048:" + keys[1].toBase64());
+ } catch (GeneralSecurityException gse) {
+ // so much for that
+ }
+ }
}
return config;
@@ -1379,13 +1422,16 @@ public class IndexBean {
I2PTunnelHTTPClient.PROP_JUMP_SERVERS,
I2PTunnelHTTPClientBase.PROP_AUTH,
I2PClient.PROP_SIGTYPE,
- I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES
+ I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES,
+ // following are mostly server but could also be persistent client
+ "inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey"
};
private static final String _otherServerOpts[] = {
"i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.leaseSetKey", "i2cp.accessList",
PROP_MAX_CONNS_MIN, PROP_MAX_CONNS_HOUR, PROP_MAX_CONNS_DAY,
PROP_MAX_TOTAL_CONNS_MIN, PROP_MAX_TOTAL_CONNS_HOUR, PROP_MAX_TOTAL_CONNS_DAY,
- PROP_MAX_STREAMS, I2PClient.PROP_SIGTYPE
+ PROP_MAX_STREAMS, I2PClient.PROP_SIGTYPE,
+ "inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey"
};
private static final String _httpServerOpts[] = {
I2PTunnelHTTPServer.OPT_POST_WINDOW,
diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp
index 3d9456d551..4c3a8d0287 100644
--- a/apps/i2ptunnel/jsp/editClient.jsp
+++ b/apps/i2ptunnel/jsp/editClient.jsp
@@ -58,6 +58,26 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
+ <%
+ // these are four keys that are generated automatically on first save,
+ // and we want to persist in i2ptunnel.config, but don't want to
+ // show clogging up the custom options form.
+ String key = editBean.getKey1(curTunnel);
+ if (key != null && key.length() > 0) { %>
+
+ <% }
+ key = editBean.getKey2(curTunnel);
+ if (key != null && key.length() > 0) { %>
+
+ <% }
+ key = editBean.getKey3(curTunnel);
+ if (key != null && key.length() > 0) { %>
+
+ <% }
+ key = editBean.getKey4(curTunnel);
+ if (key != null && key.length() > 0) { %>
+
+ <% } %>
diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp
index c1f75fc1a0..16d2bf9c00 100644
--- a/apps/i2ptunnel/jsp/editServer.jsp
+++ b/apps/i2ptunnel/jsp/editServer.jsp
@@ -58,6 +58,26 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
+ <%
+ // these are four keys that are generated automatically on first save,
+ // and we want to persist in i2ptunnel.config, but don't want to
+ // show clogging up the custom options form.
+ String key = editBean.getKey1(curTunnel);
+ if (key != null && key.length() > 0) { %>
+
+ <% }
+ key = editBean.getKey2(curTunnel);
+ if (key != null && key.length() > 0) { %>
+
+ <% }
+ key = editBean.getKey3(curTunnel);
+ if (key != null && key.length() > 0) { %>
+
+ <% }
+ key = editBean.getKey4(curTunnel);
+ if (key != null && key.length() > 0) { %>
+
+ <% } %>
diff --git a/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java b/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java
index ca72587dba..e6e9965a82 100644
--- a/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java
+++ b/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java
@@ -74,22 +74,68 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
* Finish creating and signing the new LeaseSet
* @since 0.9.7
*/
- protected void signLeaseSet(LeaseSet leaseSet, I2PSessionImpl session) {
+ protected synchronized void signLeaseSet(LeaseSet leaseSet, I2PSessionImpl session) {
+ Destination dest = session.getMyDestination();
// also, if this session is connected to multiple routers, include other leases here
- leaseSet.setDestination(session.getMyDestination());
+ leaseSet.setDestination(dest);
// reuse the old keys for the client
- LeaseInfo li = _existingLeaseSets.get(session.getMyDestination());
+ LeaseInfo li = _existingLeaseSets.get(dest);
if (li == null) {
- li = new LeaseInfo(session.getMyDestination());
- _existingLeaseSets.put(session.getMyDestination(), li);
+ // [enctype:]b64 of private key
+ String spk = session.getOptions().getProperty("i2cp.leaseSetPrivateKey");
+ // [sigtype:]b64 of private key
+ String sspk = session.getOptions().getProperty("i2cp.leaseSetSigningPrivateKey");
+ PrivateKey privKey = null;
+ SigningPrivateKey signingPrivKey = null;
+ boolean useOldKeys;
+ if (spk != null && sspk != null) {
+ useOldKeys = true;
+ int colon = sspk.indexOf(':');
+ SigType type = dest.getSigType();
+ if (colon > 0) {
+ String stype = sspk.substring(0, colon);
+ SigType t = SigType.parseSigType(stype);
+ if (t == type)
+ sspk = sspk.substring(colon + 1);
+ else
+ useOldKeys = false;
+ }
+ colon = spk.indexOf(':');
+ // just ignore for now, no other types supported
+ if (colon >= 0)
+ spk = spk.substring(colon + 1);
+ if (useOldKeys) {
+ try {
+ signingPrivKey = new SigningPrivateKey(type);
+ signingPrivKey.fromBase64(sspk);
+ } catch (DataFormatException iae) {
+ useOldKeys = false;
+ }
+ }
+ if (useOldKeys) {
+ try {
+ privKey = new PrivateKey();
+ privKey.fromBase64(spk);
+ } catch (DataFormatException iae) {
+ useOldKeys = false;
+ }
+ }
+ } else {
+ useOldKeys = false;
+ }
+ if (useOldKeys)
+ li = new LeaseInfo(privKey, signingPrivKey);
+ else
+ li = new LeaseInfo(dest);
+ _existingLeaseSets.put(dest, li);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating new leaseInfo keys for "
- + session.getMyDestination().calculateHash().toBase64());
+ + dest + " using configured private keys? " + useOldKeys);
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Caching the old leaseInfo keys for "
- + session.getMyDestination().calculateHash().toBase64());
+ + dest);
}
leaseSet.setEncryptionKey(li.getPublicKey());
@@ -133,7 +179,7 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
private final SigningPrivateKey _signingPrivKey;
public LeaseInfo(Destination dest) {
- Object encKeys[] = KeyGenerator.getInstance().generatePKIKeypair();
+ SimpleDataStructure encKeys[] = KeyGenerator.getInstance().generatePKIKeys();
// must be same type as the Destination's signing key
SimpleDataStructure signKeys[];
try {
@@ -147,6 +193,16 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
_signingPrivKey = (SigningPrivateKey) signKeys[1];
}
+ /**
+ * @since 0.9.18
+ */
+ public LeaseInfo(PrivateKey privKey, SigningPrivateKey signingPrivKey) {
+ _pubKey = KeyGenerator.getPublicKey(privKey);
+ _privKey = privKey;
+ _signingPubKey = KeyGenerator.getSigningPublicKey(signingPrivKey);
+ _signingPrivKey = signingPrivKey;
+ }
+
public PublicKey getPublicKey() {
return _pubKey;
}
diff --git a/history.txt b/history.txt
index de8ce43e71..99244811da 100644
--- a/history.txt
+++ b/history.txt
@@ -1,6 +1,10 @@
+2015-01-03 zzz
+ * I2CP: Use configured leaseset keys if available
+ * I2PTunnel: Persist leaseset keys
+
2014-12-15 zzz
* Console: Prevent two-word translations from splitting across lines in summary bar
- * EdDSA: Cleanups
+ * Crypto: EdDSA cleanups
* i2psnark: Unchoke new peer sooner
* SSU: reduce log level of uncaught errors processing I2NP message
* SU3:
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 0725033fa6..f6e6df3a5c 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -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 = 3;
+ public final static long BUILD = 4;
/** for example "-test" */
public final static String EXTRA = "";