diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java index 7583f2e3f3..60aa0178a3 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java @@ -841,18 +841,33 @@ public class TunnelConfig { _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"; - 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()); - } catch (GeneralSecurityException gse) { - // so much for that + String senc = config.getProperty(OPT + "i2cp.leaseSetEncType", "0"); + String slstyp = config.getProperty(OPT + "i2cp.leaseSetType", "0"); + if (senc.equals("0") && slstyp.equals("0")) { + // only for LS1 + p = OPT + "i2cp.leaseSetSigningPrivateKey"; + if (!config.containsKey(p)) { + SigType type; + if (_dest != null) { + type = _dest.getSigType(); + } else { + String ssigtyp = config.getProperty(OPT + "i2cp.destination.sigType", "0"); + type = SigType.parseSigType(ssigtyp); + } + if (type != null) { + try { + SimpleDataStructure keys[] = KeyGenerator.getInstance().generateSigningKeys(type); + config.setProperty(p, type.name() + ':' + keys[1].toBase64()); + } catch (GeneralSecurityException gse) { + // so much for that + } + } } } + // persistent LS encryption keys // multiple types as of 0.9.46, add missing ones p = OPT + "i2cp.leaseSetPrivateKey"; @@ -860,7 +875,6 @@ public class TunnelConfig { // normalize it first to make the code below easier if (skeys != null && skeys.length() > 0 && !skeys.contains(":")) config.setProperty(p, "ELGAMAL_2048:" + skeys); - String senc = config.getProperty(OPT + "i2cp.leaseSetEncType", "0"); String[] senca = DataHelper.split(senc, ","); // for each configured enc type, generate a key if we don't have it for (int i = 0; i < senca.length; i++) { diff --git a/core/java/src/net/i2p/client/impl/I2CPMessageProducer.java b/core/java/src/net/i2p/client/impl/I2CPMessageProducer.java index 5564747c35..c43696c906 100644 --- a/core/java/src/net/i2p/client/impl/I2CPMessageProducer.java +++ b/core/java/src/net/i2p/client/impl/I2CPMessageProducer.java @@ -9,7 +9,9 @@ package net.i2p.client.impl; * */ +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -64,7 +66,7 @@ class I2CPMessageProducer { // Note that some are listed for both client and server side, don't include those below. private static final String[] CLIENT_SIDE_OPTIONS = new String[] { "i2cp.closeIdleTime", "i2cp.closeOnIdle", "i2cp.encryptLeaseSet", - "i2cp.gzip", "i2cp.leaseSetKey", "i2cp.leaseSetPrivateKey", + I2PClient.PROP_GZIP, "i2cp.leaseSetKey", "i2cp.leaseSetPrivateKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.reduceIdleTime", "i2cp.reduceOnIdle", I2PClient.PROP_ENABLE_SSL, I2PClient.PROP_TCP_HOST, I2PClient.PROP_TCP_PORT, // long and shouldn't be passed through @@ -105,12 +107,28 @@ class I2CPMessageProducer { * @return a new copy, may be modified * @since 0.9.38 */ - private static Properties getRouterOptions(I2PSessionImpl session) { + private Properties getRouterOptions(I2PSessionImpl session) { Properties props = new Properties(); props.putAll(session.getOptions()); for (int i = 0; i < CLIENT_SIDE_OPTIONS.length; i++) { props.remove(CLIENT_SIDE_OPTIONS[i]); } + for (Iterator> iter = props.entrySet().iterator(); iter.hasNext(); ) { + // Long strings MUST be removed, even in router context, + // as the session config properties must be serialized to be signed. + // fixme, bytes could still be over 255 (unlikely) + Map.Entry e = iter.next(); + String key = (String) e.getKey(); + String val = (String) e.getValue(); + if (key.length() > 255 || val.length() > 255) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Not passing on property [" + + key + + "] in the session config, key or value is too long (max = 255): " + + val); + iter.remove(); + } + } return props; } diff --git a/core/java/src/net/i2p/client/impl/I2PSessionImpl.java b/core/java/src/net/i2p/client/impl/I2PSessionImpl.java index 24c8eed89e..720d30228d 100644 --- a/core/java/src/net/i2p/client/impl/I2PSessionImpl.java +++ b/core/java/src/net/i2p/client/impl/I2PSessionImpl.java @@ -516,18 +516,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2 continue; } String val = options.getProperty(key); - // Long strings MUST be removed, even in router context, - // as the session config properties must be serialized to be signed. - // fixme, bytes could still be over 255 (unlikely) - if (key.length() > 255 || val.length() > 255) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Not passing on property [" - + key - + "] in the session config, key or value is too long (max = 255): " - + val); - } else { - rv.setProperty(key, val); - } + rv.setProperty(key, val); } return rv; } diff --git a/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java b/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java index 3f673b34f6..00f98bd475 100644 --- a/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java +++ b/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java @@ -186,35 +186,69 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { // reuse the old keys for the client LeaseInfo li = _existingLeaseSets.get(dest); if (li == null) { + List types = new ArrayList(2); + String senc = session.getOptions().getProperty(PROP_LS_ENCTYPE); + if (senc != null) { + if (!PREFER_NEW_ENC && senc.equals("4,0")) + senc = "0,4"; + else if (PREFER_NEW_ENC && senc.equals("0,4")) + senc = "4,0"; + String[] senca = DataHelper.split(senc, ","); + for (String sencaa : senca) { + EncType newtype = EncType.parseEncType(sencaa); + if (newtype != null) { + if (types.contains(newtype)) { + _log.error("Duplicate crypto type: " + newtype); + continue; + } + if (newtype.isAvailable()) { + types.add(newtype); + } else { + _log.error("Unsupported crypto type: " + newtype); + } + } else { + _log.error("Unsupported crypto type: " + sencaa); + } + } + } + if (types.isEmpty()) { + //if (_log.shouldDebug()) + // _log.debug("Using default crypto type"); + types.add(EncType.ELGAMAL_2048); + } + // [enctype:]b64,... of private keys String spk = session.getOptions().getProperty(PROP_LS_PK); // [sigtype:]b64 of private key - String sspk = session.getOptions().getProperty(PROP_LS_SPK); + // only for LS1 + String sspk = isLS2 ? null : session.getOptions().getProperty(PROP_LS_SPK); List privKeys = new ArrayList(2); SigningPrivateKey signingPrivKey = null; - if (spk != null && sspk != null) { + if (spk != null && (isLS2 || sspk != null)) { boolean 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; - } - if (useOldKeys) { - try { - signingPrivKey = new SigningPrivateKey(type); - signingPrivKey.fromBase64(sspk); - } catch (DataFormatException dfe) { - useOldKeys = false; - signingPrivKey = null; + if (!isLS2) { + 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; + } + if (useOldKeys) { + try { + signingPrivKey = new SigningPrivateKey(type); + signingPrivKey.fromBase64(sspk); + } catch (DataFormatException dfe) { + useOldKeys = false; + signingPrivKey = null; + } } } if (useOldKeys) { - parsePrivateKeys(spk, privKeys); + parsePrivateKeys(spk, privKeys, types); } } if (privKeys.isEmpty() && !_existingLeaseSets.isEmpty()) { @@ -223,8 +257,8 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { for (Map.Entry e : _existingLeaseSets.entrySet()) { if (pk.equals(e.getKey().getPublicKey())) { privKeys.addAll(e.getValue().getPrivateKeys()); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Creating new leaseInfo keys for " + dest + " with private key from " + e.getKey()); + if (_log.shouldInfo()) + _log.info("Creating leaseInfo for " + dest.toBase32() + " with private key from " + e.getKey().toBase32()); break; } } @@ -232,53 +266,27 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { if (!privKeys.isEmpty()) { if (signingPrivKey != null) { li = new LeaseInfo(privKeys, signingPrivKey); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Creating new leaseInfo keys for " + dest + " WITH configured private keys"); + if (_log.shouldInfo()) + _log.info("Creating leaseInfo for " + dest.toBase32() + " LS1 WITH configured private keys"); + } else if (isLS2) { + li = new LeaseInfo(privKeys); + if (_log.shouldInfo()) + _log.info("Creating leaseInfo for " + dest.toBase32() + " LS2 WITH configured private keys"); } else { li = new LeaseInfo(privKeys, dest); + if (_log.shouldInfo()) + _log.info("Creating leaseInfo for " + dest.toBase32() + " LS1 WITH configured private keys and new revocation key"); } } else { - List types = new ArrayList(2); - String senc = session.getOptions().getProperty(PROP_LS_ENCTYPE); - if (senc != null) { - if (!PREFER_NEW_ENC && senc.equals("4,0")) - senc = "0,4"; - else if (PREFER_NEW_ENC && senc.equals("0,4")) - senc = "4,0"; - String[] senca = DataHelper.split(senc, ","); - for (String sencaa : senca) { - EncType newtype = EncType.parseEncType(sencaa); - if (newtype != null) { - if (types.contains(newtype)) { - _log.error("Duplicate crypto type: " + newtype); - continue; - } - if (newtype.isAvailable()) { - types.add(newtype); - if (_log.shouldDebug()) - _log.debug("Using crypto type: " + newtype); - } else { - _log.error("Unsupported crypto type: " + newtype); - } - } else { - _log.error("Unsupported crypto type: " + sencaa); - } - } - } - if (types.isEmpty()) { - if (_log.shouldDebug()) - _log.debug("Using default crypto type"); - types.add(EncType.ELGAMAL_2048); - } - li = new LeaseInfo(dest, types); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Creating new leaseInfo keys for " + dest + " without configured private keys"); + li = new LeaseInfo(dest, types, isLS2); + if (_log.shouldInfo()) + _log.info("Creating leaseInfo for " + dest.toBase32() + " without configured private keys"); } _existingLeaseSets.put(dest, li); } else { if (_log.shouldLog(Log.DEBUG)) _log.debug("Caching the old leaseInfo keys for " - + dest); + + dest.toBase32()); } if (isLS2) { @@ -438,7 +446,7 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { * @param privKeys out parameter * @since 0.9.39 */ - private void parsePrivateKeys(String spkl, List privKeys) { + private void parsePrivateKeys(String spkl, List privKeys, List allowedTypes) { String[] spks = DataHelper.split(spkl, ","); for (String spk : spks) { int colon = spk.indexOf(':'); @@ -446,14 +454,17 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { EncType type = EncType.parseEncType(spk.substring(0, colon)); if (type != null) { if (type.isAvailable()) { - try { - PrivateKey privKey = new PrivateKey(type); - privKey.fromBase64(spk.substring(colon + 1)); - privKeys.add(privKey); + if (allowedTypes.contains(type)) { + try { + PrivateKey privKey = new PrivateKey(type); + privKey.fromBase64(spk.substring(colon + 1)); + privKeys.add(privKey); + } catch (DataFormatException dfe) { + _log.error("Bad private key: " + spk, dfe); + } + } else { if (_log.shouldDebug()) - _log.debug("Using crypto type: " + type); - } catch (DataFormatException dfe) { - _log.error("Bad private key: " + spk, dfe); + _log.debug("Ignoring private key with unconfigured crypto type: " + type); } } else { _log.error("Unsupported crypto type: " + type); @@ -462,12 +473,18 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { _log.error("Unsupported crypto type: " + spk); } } else if (colon < 0) { - try { - PrivateKey privKey = new PrivateKey(); - privKey.fromBase64(spk); - privKeys.add(privKey); - } catch (DataFormatException dfe) { - _log.error("Bad private key: " + spk, dfe); + EncType type = EncType.ELGAMAL_2048; + if (allowedTypes.contains(type)) { + try { + PrivateKey privKey = new PrivateKey(); + privKey.fromBase64(spk); + privKeys.add(privKey); + } catch (DataFormatException dfe) { + _log.error("Bad private key: " + spk, dfe); + } + } else { + if (_log.shouldDebug()) + _log.debug("Ignoring private key with unconfigured crypto type: " + type); } } else { _log.error("Empty crypto type"); @@ -488,7 +505,7 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { * New keys * @param types must be available */ - public LeaseInfo(Destination dest, List types) { + public LeaseInfo(Destination dest, List types, boolean isLS2) { if (types.size() > 1 && PREFER_NEW_ENC) { Collections.sort(types, Collections.reverseOrder()); } @@ -499,19 +516,24 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { _pubKeys.add(encKeys.getPublic()); _privKeys.add(encKeys.getPrivate()); } - // must be same type as the Destination's signing key - SimpleDataStructure signKeys[]; - try { - signKeys = KeyGenerator.getInstance().generateSigningKeys(dest.getSigningPublicKey().getType()); - } catch (GeneralSecurityException gse) { - throw new IllegalStateException(gse); + if (isLS2) { + _signingPubKey = null; + _signingPrivKey = null; + } else { + // must be same type as the Destination's signing key + SimpleDataStructure signKeys[]; + try { + signKeys = KeyGenerator.getInstance().generateSigningKeys(dest.getSigningPublicKey().getType()); + } catch (GeneralSecurityException gse) { + throw new IllegalStateException(gse); + } + _signingPubKey = (SigningPublicKey) signKeys[0]; + _signingPrivKey = (SigningPrivateKey) signKeys[1]; } - _signingPubKey = (SigningPublicKey) signKeys[0]; - _signingPrivKey = (SigningPrivateKey) signKeys[1]; } /** - * Existing keys + * Existing keys, LS1 only * @param privKeys all EncTypes must be available * @since 0.9.18 */ @@ -529,7 +551,7 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { } /** - * Existing crypto keys, new signing key + * Existing crypto keys, new signing key, LS1 only * @param privKeys all EncTypes must be available * @since 0.9.21 */ @@ -549,6 +571,24 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { _signingPrivKey = (SigningPrivateKey) signKeys[1]; } + /** + * Existing keys, LS2 only + * @param privKeys all EncTypes must be available + * @since 0.9.47 + */ + public LeaseInfo(List privKeys) { + if (privKeys.size() > 1) { + Collections.sort(privKeys, new PrivKeyComparator()); + } + _privKeys = privKeys; + _pubKeys = new ArrayList(privKeys.size()); + for (PrivateKey privKey : privKeys) { + _pubKeys.add(KeyGenerator.getPublicKey(privKey)); + } + _signingPubKey = null; + _signingPrivKey = null; + } + /** @return the first one if more than one */ public PublicKey getPublicKey() { return _pubKeys.get(0); @@ -569,10 +609,12 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { return _privKeys; } + /** @return null for LS2 */ public SigningPublicKey getSigningPublicKey() { return _signingPubKey; } + /** @return null for LS2 */ public SigningPrivateKey getSigningPrivateKey() { return _signingPrivKey; }