i2cp.leaseSetEncType param:

Support comma-separated values,
check param in session config,
support stored EncTypes in private keys.
Show types and multiple keys on LS debug page
This commit is contained in:
zzz
2019-01-30 19:33:15 +00:00
parent 1bb57c4103
commit c555bb6c93
5 changed files with 190 additions and 69 deletions

View File

@ -25,6 +25,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import net.i2p.crypto.EncType;
import net.i2p.crypto.SigType; import net.i2p.crypto.SigType;
import net.i2p.data.DatabaseEntry; import net.i2p.data.DatabaseEntry;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
@ -33,6 +34,7 @@ import net.i2p.data.Hash;
import net.i2p.data.Lease; import net.i2p.data.Lease;
import net.i2p.data.LeaseSet; import net.i2p.data.LeaseSet;
import net.i2p.data.LeaseSet2; import net.i2p.data.LeaseSet2;
import net.i2p.data.PublicKey;
import net.i2p.data.router.RouterAddress; import net.i2p.data.router.RouterAddress;
import net.i2p.data.router.RouterInfo; import net.i2p.data.router.RouterInfo;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
@ -533,8 +535,24 @@ class NetDbRenderer {
buf.append("</td></tr>\n<tr><td colspan=\"2\">"); buf.append("</td></tr>\n<tr><td colspan=\"2\">");
//buf.append(dest.toBase32()).append("<br>"); //buf.append(dest.toBase32()).append("<br>");
buf.append("<b>Signature type:</b> ").append(dest.getSigningPublicKey().getType()); buf.append("<b>Signature type:</b> ").append(dest.getSigningPublicKey().getType());
if (type != DatabaseEntry.KEY_TYPE_META_LS2) if (type == DatabaseEntry.KEY_TYPE_LEASESET) {
buf.append("&nbsp;&nbsp;<b>Encryption Key:</b> ").append(ls.getEncryptionKey().toBase64().substring(0, 20)).append("&hellip;"); buf.append("</td></tr>\n<tr><td colspan=\"2\"><b>Encryption Key:</b> ELGAMAL_2048 ")
.append(ls.getEncryptionKey().toBase64().substring(0, 20))
.append("&hellip;");
} else if (type == DatabaseEntry.KEY_TYPE_LS2) {
LeaseSet2 ls2 = (LeaseSet2) ls;
for (PublicKey pk : ls2.getEncryptionKeys()) {
buf.append("</td></tr>\n<tr><td colspan=\"2\"><b>Encryption Key:</b> ");
EncType etype = pk.getType();
if (etype != null)
buf.append(etype);
else
buf.append("Unsupported type ").append(pk.getUnknownTypeCode());
buf.append(' ')
.append(pk.toBase64().substring(0, 20))
.append("&hellip;");
}
}
buf.append("</td></tr>\n<tr><td colspan=\"2\">"); buf.append("</td></tr>\n<tr><td colspan=\"2\">");
buf.append("<b>Routing Key:</b> ").append(ls.getRoutingKey().toBase64()); buf.append("<b>Routing Key:</b> ").append(ls.getRoutingKey().toBase64());
buf.append("</td></tr>"); buf.append("</td></tr>");

View File

@ -11,6 +11,10 @@ package net.i2p.client.impl;
import java.io.EOFException; import java.io.EOFException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -90,8 +94,7 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
return true; return true;
String s = session.getOptions().getProperty(PROP_LS_ENCTYPE); String s = session.getOptions().getProperty(PROP_LS_ENCTYPE);
if (s != null) { if (s != null) {
EncType type = EncType.parseEncType(s); if (!s.equals("0") && !s.equals("ELGAMAL_2048"))
if (type != null && type != EncType.ELGAMAL_2048 && type.isAvailable())
return true; return true;
} }
s = session.getOptions().getProperty(PROP_LS_TYPE); s = session.getOptions().getProperty(PROP_LS_TYPE);
@ -150,14 +153,14 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
//lease.setStartDate(msg.getStartDate()); //lease.setStartDate(msg.getStartDate());
leaseSet.addLease(lease); leaseSet.addLease(lease);
} }
signLeaseSet(leaseSet, session); signLeaseSet(leaseSet, isLS2, session);
} }
/** /**
* Finish creating and signing the new LeaseSet * Finish creating and signing the new LeaseSet
* @since 0.9.7 * @since 0.9.7
*/ */
protected synchronized void signLeaseSet(LeaseSet leaseSet, I2PSessionImpl session) { protected synchronized void signLeaseSet(LeaseSet leaseSet, boolean isLS2, I2PSessionImpl session) {
Destination dest = session.getMyDestination(); Destination dest = session.getMyDestination();
// also, if this session is connected to multiple routers, include other leases here // also, if this session is connected to multiple routers, include other leases here
leaseSet.setDestination(dest); leaseSet.setDestination(dest);
@ -165,11 +168,11 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
// reuse the old keys for the client // reuse the old keys for the client
LeaseInfo li = _existingLeaseSets.get(dest); LeaseInfo li = _existingLeaseSets.get(dest);
if (li == null) { if (li == null) {
// [enctype:]b64 of private key // [enctype:]b64,... of private keys
String spk = session.getOptions().getProperty(PROP_LS_PK); String spk = session.getOptions().getProperty(PROP_LS_PK);
// [sigtype:]b64 of private key // [sigtype:]b64 of private key
String sspk = session.getOptions().getProperty(PROP_LS_SPK); String sspk = session.getOptions().getProperty(PROP_LS_SPK);
PrivateKey privKey = null; List<PrivateKey> privKeys = new ArrayList<PrivateKey>(2);
SigningPrivateKey signingPrivKey = null; SigningPrivateKey signingPrivKey = null;
if (spk != null && sspk != null) { if (spk != null && sspk != null) {
boolean useOldKeys = true; boolean useOldKeys = true;
@ -183,69 +186,69 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
else else
useOldKeys = false; useOldKeys = false;
} }
colon = spk.indexOf(':');
// just ignore for now, no other types supported
if (colon >= 0)
spk = spk.substring(colon + 1);
if (useOldKeys) { if (useOldKeys) {
try { try {
signingPrivKey = new SigningPrivateKey(type); signingPrivKey = new SigningPrivateKey(type);
signingPrivKey.fromBase64(sspk); signingPrivKey.fromBase64(sspk);
} catch (DataFormatException iae) { } catch (DataFormatException dfe) {
useOldKeys = false; useOldKeys = false;
signingPrivKey = null; signingPrivKey = null;
} }
} }
if (useOldKeys) { if (useOldKeys) {
try { parsePrivateKeys(spk, privKeys);
privKey = new PrivateKey();
privKey.fromBase64(spk);
} catch (DataFormatException iae) {
privKey = null;
}
} }
} }
if (privKey == null && !_existingLeaseSets.isEmpty()) { if (privKeys.isEmpty() && !_existingLeaseSets.isEmpty()) {
// look for keypair from another dest using same pubkey // look for private keys from another dest using same pubkey
PublicKey pk = dest.getPublicKey(); PublicKey pk = dest.getPublicKey();
for (Map.Entry<Destination, LeaseInfo> e : _existingLeaseSets.entrySet()) { for (Map.Entry<Destination, LeaseInfo> e : _existingLeaseSets.entrySet()) {
if (pk.equals(e.getKey().getPublicKey())) { if (pk.equals(e.getKey().getPublicKey())) {
privKey = e.getValue().getPrivateKey(); privKeys.addAll(e.getValue().getPrivateKeys());
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating new leaseInfo keys for " + dest + " with private key from " + e.getKey()); _log.debug("Creating new leaseInfo keys for " + dest + " with private key from " + e.getKey());
break; break;
} }
} }
} }
if (privKey != null) { if (!privKeys.isEmpty()) {
if (signingPrivKey != null) { if (signingPrivKey != null) {
li = new LeaseInfo(privKey, signingPrivKey); li = new LeaseInfo(privKeys, signingPrivKey);
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating new leaseInfo keys for " + dest + " WITH configured private keys"); _log.debug("Creating new leaseInfo keys for " + dest + " WITH configured private keys");
} else { } else {
li = new LeaseInfo(privKey, dest); li = new LeaseInfo(privKeys, dest);
} }
} else { } else {
EncType type = EncType.ELGAMAL_2048; List<EncType> types = new ArrayList<EncType>(2);
String senc = session.getOptions().getProperty(PROP_LS_ENCTYPE); String senc = session.getOptions().getProperty(PROP_LS_ENCTYPE);
if (senc != null) { if (senc != null) {
EncType newtype = EncType.parseEncType(senc); String[] senca = DataHelper.split(senc, ",");
if (newtype != null) { for (String sencaa : senca) {
if (newtype.isAvailable()) { EncType newtype = EncType.parseEncType(sencaa);
type = newtype; if (newtype != null) {
if (_log.shouldDebug()) if (types.contains(newtype)) {
_log.debug("Using crypto type: " + type); _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 { } else {
_log.error("Unsupported crypto.encType: " + newtype); _log.error("Unsupported crypto type: " + sencaa);
} }
} else {
_log.error("Bad crypto.encType: " + senc);
} }
} else { }
if (types.isEmpty()) {
if (_log.shouldDebug()) if (_log.shouldDebug())
_log.debug("Using default crypto type"); _log.debug("Using default crypto type");
types.add(EncType.ELGAMAL_2048);
} }
li = new LeaseInfo(dest, type); li = new LeaseInfo(dest, types);
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating new leaseInfo keys for " + dest + " without configured private keys"); _log.debug("Creating new leaseInfo keys for " + dest + " without configured private keys");
} }
@ -256,8 +259,16 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
+ dest); + dest);
} }
if (_ls2Type != DatabaseEntry.KEY_TYPE_META_LS2) if (isLS2) {
LeaseSet2 ls2 = (LeaseSet2) leaseSet;
if (_ls2Type != DatabaseEntry.KEY_TYPE_META_LS2) {
for (PublicKey key : li.getPublicKeys()) {
ls2.addEncryptionKey(key);
}
}
} else {
leaseSet.setEncryptionKey(li.getPublicKey()); leaseSet.setEncryptionKey(li.getPublicKey());
}
leaseSet.setSigningKey(li.getSigningPublicKey()); leaseSet.setSigningKey(li.getSigningPublicKey());
// SubSession options aren't updated via the gui, so use the primary options // SubSession options aren't updated via the gui, so use the primary options
Properties opts; Properties opts;
@ -325,17 +336,72 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
} }
} }
/**
* @param spk non-null [type:]b64[,[type:]b64]...
* @param privKeys out parameter
* @since 0.9.39
*/
private void parsePrivateKeys(String spkl, List<PrivateKey> privKeys) {
String[] spks = DataHelper.split(spkl, ",");
for (String spk : spks) {
int colon = spk.indexOf(':');
if (colon > 0) {
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 (_log.shouldDebug())
_log.debug("Using crypto type: " + type);
} catch (DataFormatException dfe) {
_log.error("Bad private key: " + spk, dfe);
}
} else {
_log.error("Unsupported crypto type: " + type);
}
} else {
_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);
}
} else {
_log.error("Empty crypto type");
}
}
}
/**
* Multiple encryption keys supported, as of 0.9.39, for LS2
*/
private static class LeaseInfo { private static class LeaseInfo {
private final PublicKey _pubKey; private final List<PublicKey> _pubKeys;
private final PrivateKey _privKey; private final List<PrivateKey> _privKeys;
private final SigningPublicKey _signingPubKey; private final SigningPublicKey _signingPubKey;
private final SigningPrivateKey _signingPrivKey; private final SigningPrivateKey _signingPrivKey;
/** /**
* New keys * New keys
* @param types must be available
*/ */
public LeaseInfo(Destination dest, EncType type) { public LeaseInfo(Destination dest, List<EncType> types) {
KeyPair encKeys = KeyGenerator.getInstance().generatePKIKeys(type); if (types.size() > 1) {
Collections.sort(types, Collections.reverseOrder());
}
_privKeys = new ArrayList<PrivateKey>(types.size());
_pubKeys = new ArrayList<PublicKey>(types.size());
for (EncType type : types) {
KeyPair encKeys = KeyGenerator.getInstance().generatePKIKeys(type);
_pubKeys.add(encKeys.getPublic());
_privKeys.add(encKeys.getPrivate());
}
// must be same type as the Destination's signing key // must be same type as the Destination's signing key
SimpleDataStructure signKeys[]; SimpleDataStructure signKeys[];
try { try {
@ -343,46 +409,67 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
} catch (GeneralSecurityException gse) { } catch (GeneralSecurityException gse) {
throw new IllegalStateException(gse); throw new IllegalStateException(gse);
} }
_pubKey = encKeys.getPublic();
_privKey = encKeys.getPrivate();
_signingPubKey = (SigningPublicKey) signKeys[0]; _signingPubKey = (SigningPublicKey) signKeys[0];
_signingPrivKey = (SigningPrivateKey) signKeys[1]; _signingPrivKey = (SigningPrivateKey) signKeys[1];
} }
/** /**
* Existing keys * Existing keys
* @param privKeys all EncTypes must be available
* @since 0.9.18 * @since 0.9.18
*/ */
public LeaseInfo(PrivateKey privKey, SigningPrivateKey signingPrivKey) { public LeaseInfo(List<PrivateKey> privKeys, SigningPrivateKey signingPrivKey) {
_pubKey = KeyGenerator.getPublicKey(privKey); if (privKeys.size() > 1) {
_privKey = privKey; Collections.sort(privKeys, new PrivKeyComparator());
}
_privKeys = privKeys;
_pubKeys = new ArrayList<PublicKey>(privKeys.size());
for (PrivateKey privKey : privKeys) {
_pubKeys.add(KeyGenerator.getPublicKey(privKey));
}
_signingPubKey = KeyGenerator.getSigningPublicKey(signingPrivKey); _signingPubKey = KeyGenerator.getSigningPublicKey(signingPrivKey);
_signingPrivKey = signingPrivKey; _signingPrivKey = signingPrivKey;
} }
/** /**
* Existing crypto key, new signing key * Existing crypto keys, new signing key
* @param privKeys all EncTypes must be available
* @since 0.9.21 * @since 0.9.21
*/ */
public LeaseInfo(PrivateKey privKey, Destination dest) { public LeaseInfo(List<PrivateKey> privKeys, Destination dest) {
SimpleDataStructure signKeys[]; SimpleDataStructure signKeys[];
try { try {
signKeys = KeyGenerator.getInstance().generateSigningKeys(dest.getSigningPublicKey().getType()); signKeys = KeyGenerator.getInstance().generateSigningKeys(dest.getSigningPublicKey().getType());
} catch (GeneralSecurityException gse) { } catch (GeneralSecurityException gse) {
throw new IllegalStateException(gse); throw new IllegalStateException(gse);
} }
_pubKey = KeyGenerator.getPublicKey(privKey); _privKeys = privKeys;
_privKey = privKey; _pubKeys = new ArrayList<PublicKey>(privKeys.size());
for (PrivateKey privKey : privKeys) {
_pubKeys.add(KeyGenerator.getPublicKey(privKey));
}
_signingPubKey = (SigningPublicKey) signKeys[0]; _signingPubKey = (SigningPublicKey) signKeys[0];
_signingPrivKey = (SigningPrivateKey) signKeys[1]; _signingPrivKey = (SigningPrivateKey) signKeys[1];
} }
/** @return the first one if more than one */
public PublicKey getPublicKey() { public PublicKey getPublicKey() {
return _pubKey; return _pubKeys.get(0);
} }
/** @return the first one if more than one */
public PrivateKey getPrivateKey() { public PrivateKey getPrivateKey() {
return _privKey; return _privKeys.get(0);
}
/** @since 0.9.39 */
public List<PublicKey> getPublicKeys() {
return _pubKeys;
}
/** @since 0.9.39 */
public List<PrivateKey> getPrivateKeys() {
return _privKeys;
} }
public SigningPublicKey getSigningPublicKey() { public SigningPublicKey getSigningPublicKey() {
@ -392,20 +479,15 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
public SigningPrivateKey getSigningPrivateKey() { public SigningPrivateKey getSigningPrivateKey() {
return _signingPrivKey; return _signingPrivKey;
} }
@Override /**
public int hashCode() { * Reverse order by enc type
return DataHelper.hashCode(_pubKey) + 7 * DataHelper.hashCode(_privKey) + 7 * 7 * @since 0.9.39
* DataHelper.hashCode(_signingPubKey) + 7 * 7 * 7 * DataHelper.hashCode(_signingPrivKey); */
} private static class PrivKeyComparator implements Comparator<PrivateKey> {
public int compare(PrivateKey l, PrivateKey r) {
@Override return r.getType().compareTo(l.getType());
public boolean equals(Object obj) { }
if ((obj == null) || !(obj instanceof LeaseInfo)) return false;
LeaseInfo li = (LeaseInfo) obj;
return DataHelper.eq(_pubKey, li.getPublicKey()) && DataHelper.eq(_privKey, li.getPrivateKey())
&& DataHelper.eq(_signingPubKey, li.getSigningPublicKey())
&& DataHelper.eq(_signingPrivKey, li.getSigningPrivateKey());
} }
} }
} }

View File

@ -77,6 +77,6 @@ class RequestVariableLeaseSetMessageHandler extends RequestLeaseSetMessageHandle
} }
leaseSet.addLease(lease); leaseSet.addLease(lease);
} }
signLeaseSet(leaseSet, session); signLeaseSet(leaseSet, isLS2, session);
} }
} }

View File

@ -555,7 +555,7 @@ public class LeaseSet2 extends LeaseSet {
int sz = keys.size(); int sz = keys.size();
buf.append("\n\tEncryption Keys: ").append(sz); buf.append("\n\tEncryption Keys: ").append(sz);
for (int i = 0; i < sz; i++) { for (int i = 0; i < sz; i++) {
buf.append("\n\tEncryptionKey ").append(i).append(": ").append(keys.get(i)); buf.append("\n\tEncryption Key ").append(i).append(": ").append(keys.get(i));
} }
if (isOffline()) { if (isOffline()) {
buf.append("\n\tTransient Key: ").append(_transientSigningPublicKey); buf.append("\n\tTransient Key: ").append(_transientSigningPublicKey);

View File

@ -12,6 +12,7 @@ import java.util.List;
import java.util.Properties; import java.util.Properties;
import net.i2p.CoreVersion; import net.i2p.CoreVersion;
import net.i2p.crypto.EncType;
import net.i2p.crypto.SigType; import net.i2p.crypto.SigType;
import net.i2p.data.DatabaseEntry; import net.i2p.data.DatabaseEntry;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
@ -268,6 +269,26 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
} }
} }
props.putAll(inProps); props.putAll(inProps);
String senc = props.getProperty("i2cp.leaseSetEncType");
if (senc != null) {
String[] senca = DataHelper.split(senc, ",");
for (String sencaa : senca) {
EncType type = EncType.parseEncType(sencaa);
if (type != null) {
if (!type.isAvailable()) {
String msg = "Unsupported crypto type: " + type;
_log.error(msg);
_runner.disconnectClient(msg);
return;
}
} else {
String msg = "Unsupported crypto type: " + sencaa;
_log.error(msg);
_runner.disconnectClient(msg);
return;
}
}
}
if ("7".equals(props.getProperty("i2cp.leaseSetType"))) { if ("7".equals(props.getProperty("i2cp.leaseSetType"))) {
// Prevent tunnel builds for Meta LS // Prevent tunnel builds for Meta LS
// more TODO // more TODO