I2CP: Add preliminary support for LS2 (proposal 123)

This commit is contained in:
zzz
2018-12-01 11:40:10 +00:00
parent 922515dfe4
commit 79440f84eb
8 changed files with 230 additions and 24 deletions

View File

@@ -17,6 +17,7 @@ import java.util.concurrent.locks.ReentrantLock;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSessionException;
import net.i2p.client.SendMessageOptions;
import net.i2p.data.DatabaseEntry;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.data.LeaseSet;
@@ -28,6 +29,7 @@ import net.i2p.data.SigningPrivateKey;
import net.i2p.data.i2cp.AbuseReason;
import net.i2p.data.i2cp.AbuseSeverity;
import net.i2p.data.i2cp.CreateLeaseSetMessage;
import net.i2p.data.i2cp.CreateLeaseSet2Message;
import net.i2p.data.i2cp.CreateSessionMessage;
import net.i2p.data.i2cp.DestroySessionMessage;
import net.i2p.data.i2cp.MessageId;
@@ -328,13 +330,20 @@ class I2CPMessageProducer {
}
/**
* Create a new signed leaseSet in response to a request to do so and send it
* to the router
*
* In response to a RequestLeaseSet Message from the router, send a
* CreateLeaseset Message back to the router.
* This method is misnamed, it does not create the LeaseSet,
* the caller does that.
*
*/
public void createLeaseSet(I2PSessionImpl session, LeaseSet leaseSet, SigningPrivateKey signingPriv,
PrivateKey priv) throws I2PSessionException {
CreateLeaseSetMessage msg = new CreateLeaseSetMessage();
CreateLeaseSetMessage msg;
int type = leaseSet.getType();
if (type == DatabaseEntry.KEY_TYPE_LEASESET)
msg = new CreateLeaseSetMessage();
else
msg = new CreateLeaseSet2Message();
msg.setLeaseSet(leaseSet);
msg.setPrivateKey(priv);
msg.setSigningPrivateKey(signingPriv);

View File

@@ -171,6 +171,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
private final boolean _fastReceive;
private volatile boolean _routerSupportsFastReceive;
private volatile boolean _routerSupportsHostLookup;
private volatile boolean _routerSupportsLS2;
protected static final int CACHE_MAX_SIZE = SystemVersion.isAndroid() ? 32 : 128;
/**
@@ -197,19 +198,25 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
private static final long MAX_SEND_WAIT = 10*1000;
private static final String MIN_FAST_VERSION = "0.9.4";
////// TESTING, change to 38 before release
private static final String MIN_LS2_VERSION = "0.9.37";
/** @param routerVersion as rcvd in the SetDateMessage, may be null for very old routers */
void dateUpdated(String routerVersion) {
_routerSupportsFastReceive = _context.isRouterContext() ||
boolean isrc = _context.isRouterContext();
_routerSupportsFastReceive = isrc ||
(routerVersion != null && routerVersion.length() > 0 &&
VersionComparator.comp(routerVersion, MIN_FAST_VERSION) >= 0);
_routerSupportsHostLookup = _context.isRouterContext() ||
_routerSupportsHostLookup = isrc ||
TEST_LOOKUP ||
(routerVersion != null && routerVersion.length() > 0 &&
VersionComparator.comp(routerVersion, MIN_HOST_LOOKUP_VERSION) >= 0);
_routerSupportsSubsessions = _context.isRouterContext() ||
_routerSupportsSubsessions = isrc ||
(routerVersion != null && routerVersion.length() > 0 &&
VersionComparator.comp(routerVersion, MIN_SUBSESSION_VERSION) >= 0);
_routerSupportsLS2 = isrc ||
(routerVersion != null && routerVersion.length() > 0 &&
VersionComparator.comp(routerVersion, MIN_LS2_VERSION) >= 0);
synchronized (_stateLock) {
if (_state == State.OPENING) {
changeState(State.GOTDATE);
@@ -281,9 +288,11 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
_privateKey = null;
_signingPrivateKey = null;
}
_routerSupportsFastReceive = _context.isRouterContext();
_routerSupportsHostLookup = _context.isRouterContext();
_routerSupportsSubsessions = _context.isRouterContext();
boolean isrc = _context.isRouterContext();
_routerSupportsFastReceive = isrc;
_routerSupportsHostLookup = isrc;
_routerSupportsSubsessions = isrc;
_routerSupportsLS2 = isrc;
}
/**
@@ -509,6 +518,13 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
return _fastReceive && _routerSupportsFastReceive;
}
/**
* @since 0.9.38
*/
public boolean supportsLS2() {
return _routerSupportsLS2;
}
void setLeaseSet(LeaseSet ls) {
_leaseSet = ls;
if (ls != null) {

View File

@@ -17,14 +17,19 @@ import java.util.concurrent.ConcurrentHashMap;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSessionException;
import net.i2p.crypto.EncType;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.KeyPair;
import net.i2p.crypto.SigType;
import net.i2p.data.DatabaseEntry;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.Lease;
import net.i2p.data.Lease2;
import net.i2p.data.LeaseSet;
import net.i2p.data.LeaseSet2;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey;
@@ -58,13 +63,38 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
_existingLeaseSets = new ConcurrentHashMap<Destination, LeaseInfo>(4);
}
/**
* Do we send a LeaseSet or a LeaseSet2?
* @since 0.9.38
*/
protected static boolean requiresLS2(I2PSessionImpl session) {
if (!session.supportsLS2())
return false;
String s = session.getOptions().getProperty("crypto.encType");
if (s != null) {
EncType type = EncType.parseEncType(s);
if (type != null && type != EncType.ELGAMAL_2048 && type.isAvailable())
return true;
}
s = session.getOptions().getProperty("i2cp.leaseSetType");
if (s != null) {
try {
int type = Integer.parseInt(s);
if (type != DatabaseEntry.KEY_TYPE_LEASESET)
return true;
} catch (NumberFormatException nfe) {}
}
return false;
}
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handle message " + message);
RequestLeaseSetMessage msg = (RequestLeaseSetMessage) message;
LeaseSet leaseSet = new LeaseSet();
boolean isLS2 = requiresLS2(session);
LeaseSet leaseSet = isLS2 ? new LeaseSet2() : new LeaseSet();
for (int i = 0; i < msg.getEndpoints(); i++) {
Lease lease = new Lease();
Lease lease = isLS2 ? new Lease2() : new Lease();
lease.setGateway(msg.getRouter(i));
lease.setTunnelId(msg.getTunnelId(i));
lease.setEndDate(msg.getEndDate());
@@ -147,7 +177,26 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
li = new LeaseInfo(privKey, dest);
}
} else {
li = new LeaseInfo(dest);
EncType type = EncType.ELGAMAL_2048;
String senc = session.getOptions().getProperty("crypto.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);
} else {
_log.error("Unsupported crypto.encType: " + newtype);
}
} else {
_log.error("Bad crypto.encType: " + senc);
}
} else {
if (_log.shouldDebug())
_log.debug("Using default crypto type");
}
li = new LeaseInfo(dest, type);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating new leaseInfo keys for " + dest + " without configured private keys");
}
@@ -187,14 +236,18 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
// Workaround for unparsable serialized signing private key for revocation
// Send him a dummy DSA_SHA1 private key since it's unused anyway
// See CreateLeaseSetMessage.doReadMessage()
// For LS1 only
SigningPrivateKey spk = li.getSigningPrivateKey();
if (!_context.isRouterContext() && spk.getType() != SigType.DSA_SHA1) {
if (!_context.isRouterContext() && spk.getType() != SigType.DSA_SHA1 &&
!(leaseSet instanceof LeaseSet2)) {
byte[] dummy = new byte[SigningPrivateKey.KEYSIZE_BYTES];
_context.random().nextBytes(dummy);
spk = new SigningPrivateKey(dummy);
}
session.getProducer().createLeaseSet(session, leaseSet, spk, li.getPrivateKey());
session.setLeaseSet(leaseSet);
if (_log.shouldDebug())
_log.debug("Created and signed LeaseSet: " + leaseSet);
} catch (DataFormatException dfe) {
session.propogateError("Error signing the leaseSet", dfe);
} catch (I2PSessionException ise) {
@@ -220,8 +273,8 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
/**
* New keys
*/
public LeaseInfo(Destination dest) {
SimpleDataStructure encKeys[] = KeyGenerator.getInstance().generatePKIKeys();
public LeaseInfo(Destination dest, EncType type) {
KeyPair encKeys = KeyGenerator.getInstance().generatePKIKeys(type);
// must be same type as the Destination's signing key
SimpleDataStructure signKeys[];
try {
@@ -229,8 +282,8 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
} catch (GeneralSecurityException gse) {
throw new IllegalStateException(gse);
}
_pubKey = (PublicKey) encKeys[0];
_privKey = (PrivateKey) encKeys[1];
_pubKey = encKeys.getPublic();
_privKey = encKeys.getPrivate();
_signingPubKey = (SigningPublicKey) signKeys[0];
_signingPrivKey = (SigningPrivateKey) signKeys[1];
}

View File

@@ -10,7 +10,10 @@ package net.i2p.client.impl;
*/
import net.i2p.I2PAppContext;
import net.i2p.data.Lease;
import net.i2p.data.Lease2;
import net.i2p.data.LeaseSet;
import net.i2p.data.LeaseSet2;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.RequestVariableLeaseSetMessage;
import net.i2p.util.Log;
@@ -32,9 +35,21 @@ class RequestVariableLeaseSetMessageHandler extends RequestLeaseSetMessageHandle
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handle message " + message);
RequestVariableLeaseSetMessage msg = (RequestVariableLeaseSetMessage) message;
LeaseSet leaseSet = new LeaseSet();
boolean isLS2 = requiresLS2(session);
LeaseSet leaseSet = isLS2 ? new LeaseSet2() : new LeaseSet();
for (int i = 0; i < msg.getEndpoints(); i++) {
leaseSet.addLease(msg.getEndpoint(i));
Lease lease;
if (isLS2) {
// convert Lease to Lease2
Lease old = msg.getEndpoint(i);
lease = new Lease2();
lease.setGateway(old.getGateway());
lease.setTunnelId(old.getTunnelId());
lease.setEndDate(old.getEndDate());
} else {
lease = msg.getEndpoint(i);
}
leaseSet.addLease(lease);
}
signLeaseSet(leaseSet, session);
}