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