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.TreeSet;
import net.i2p.crypto.EncType;
import net.i2p.crypto.SigType;
import net.i2p.data.DatabaseEntry;
import net.i2p.data.DataHelper;
@ -33,6 +34,7 @@ import net.i2p.data.Hash;
import net.i2p.data.Lease;
import net.i2p.data.LeaseSet;
import net.i2p.data.LeaseSet2;
import net.i2p.data.PublicKey;
import net.i2p.data.router.RouterAddress;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.RouterContext;
@ -533,8 +535,24 @@ class NetDbRenderer {
buf.append("</td></tr>\n<tr><td colspan=\"2\">");
//buf.append(dest.toBase32()).append("<br>");
buf.append("<b>Signature type:</b> ").append(dest.getSigningPublicKey().getType());
if (type != DatabaseEntry.KEY_TYPE_META_LS2)
buf.append("&nbsp;&nbsp;<b>Encryption Key:</b> ").append(ls.getEncryptionKey().toBase64().substring(0, 20)).append("&hellip;");
if (type == DatabaseEntry.KEY_TYPE_LEASESET) {
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("<b>Routing Key:</b> ").append(ls.getRoutingKey().toBase64());
buf.append("</td></tr>");

View File

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

View File

@ -77,6 +77,6 @@ class RequestVariableLeaseSetMessageHandler extends RequestLeaseSetMessageHandle
}
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();
buf.append("\n\tEncryption Keys: ").append(sz);
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()) {
buf.append("\n\tTransient Key: ").append(_transientSigningPublicKey);

View File

@ -12,6 +12,7 @@ import java.util.List;
import java.util.Properties;
import net.i2p.CoreVersion;
import net.i2p.crypto.EncType;
import net.i2p.crypto.SigType;
import net.i2p.data.DatabaseEntry;
import net.i2p.data.DataHelper;
@ -268,6 +269,26 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
}
}
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"))) {
// Prevent tunnel builds for Meta LS
// more TODO