forked from I2P_Developers/i2p.i2p
I2CP: Add preliminary support for LS2 (proposal 123)
This commit is contained in:
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
109
core/java/src/net/i2p/data/i2cp/CreateLeaseSet2Message.java
Normal file
109
core/java/src/net/i2p/data/i2cp/CreateLeaseSet2Message.java
Normal file
@ -0,0 +1,109 @@
|
||||
package net.i2p.data.i2cp;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.crypto.EncType;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DatabaseEntry;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.LeaseSet2;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
|
||||
/**
|
||||
* Like CreateLeaseSetMessage, but supports both old
|
||||
* and new LeaseSet types, including LS2, Meta, and Encrypted.
|
||||
*
|
||||
* For LS2:
|
||||
* Same as CreateLeaseSetMessage, but has a netdb type before
|
||||
* the LeaseSet. SigningPrivateKey and PrivateKey are
|
||||
* serialized after the LeaseSet, not before, so we can
|
||||
* infer the types from the LeaseSet.
|
||||
*
|
||||
* @since 0.9.38
|
||||
*/
|
||||
public class CreateLeaseSet2Message extends CreateLeaseSetMessage {
|
||||
public final static int MESSAGE_TYPE = 40;
|
||||
|
||||
public CreateLeaseSet2Message() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||
try {
|
||||
_sessionId = new SessionId();
|
||||
_sessionId.readBytes(in);
|
||||
int type = in.read();
|
||||
if (type == DatabaseEntry.KEY_TYPE_LEASESET) {
|
||||
_leaseSet = new LeaseSet();
|
||||
} else if (type == DatabaseEntry.KEY_TYPE_LS2) {
|
||||
_leaseSet = new LeaseSet2();
|
||||
} else if (type == -1) {
|
||||
throw new EOFException("EOF reading LS type");
|
||||
} else {
|
||||
throw new I2CPMessageException("Unsupported Leaseset type: " + type);
|
||||
}
|
||||
_leaseSet.readBytes(in);
|
||||
// In CLSM this is the type of the dest, but revocation is unimplemented.
|
||||
// In CLS2M this is the type of the signature (which may be different than the
|
||||
// type of the dest if it's an offline signature)
|
||||
// and is needed by the session tag manager.
|
||||
SigType stype = _leaseSet.getSignature().getType();
|
||||
if (stype == null)
|
||||
throw new I2CPMessageException("Unsupported sig type");
|
||||
_signingPrivateKey = new SigningPrivateKey(stype);
|
||||
_signingPrivateKey.readBytes(in);
|
||||
EncType etype = _leaseSet.getEncryptionKey().getType();
|
||||
if (etype == null)
|
||||
throw new I2CPMessageException("Unsupported encryption type");
|
||||
_privateKey = new PrivateKey(etype);
|
||||
_privateKey.readBytes(in);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2CPMessageException("Error reading the CreateLeaseSetMessage", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||
if (_sessionId == null || _signingPrivateKey == null || _privateKey == null || _leaseSet == null)
|
||||
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||
int size = 4 // sessionId
|
||||
+ 1 // type
|
||||
+ _leaseSet.size()
|
||||
+ _signingPrivateKey.length()
|
||||
+ _privateKey.length();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(size);
|
||||
try {
|
||||
_sessionId.writeBytes(os);
|
||||
os.write(_leaseSet.getType());
|
||||
_leaseSet.writeBytes(os);
|
||||
_signingPrivateKey.writeBytes(os);
|
||||
_privateKey.writeBytes(os);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2CPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return MESSAGE_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[CreateLeaseSet2Message: ");
|
||||
buf.append("\n\tLeaseSet: ").append(getLeaseSet());
|
||||
buf.append("\n\tSigningPrivateKey: ").append(getSigningPrivateKey());
|
||||
buf.append("\n\tPrivateKey: ").append(getPrivateKey());
|
||||
buf.append("\n\tSessionId: ").append(getSessionId());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -26,10 +26,10 @@ import net.i2p.data.SigningPrivateKey;
|
||||
*/
|
||||
public class CreateLeaseSetMessage extends I2CPMessageImpl {
|
||||
public final static int MESSAGE_TYPE = 4;
|
||||
private SessionId _sessionId;
|
||||
private LeaseSet _leaseSet;
|
||||
private SigningPrivateKey _signingPrivateKey;
|
||||
private PrivateKey _privateKey;
|
||||
protected SessionId _sessionId;
|
||||
protected LeaseSet _leaseSet;
|
||||
protected SigningPrivateKey _signingPrivateKey;
|
||||
protected PrivateKey _privateKey;
|
||||
|
||||
public CreateLeaseSetMessage() {
|
||||
}
|
||||
|
@ -112,6 +112,8 @@ public class I2CPMessageHandler {
|
||||
return new HostLookupMessage();
|
||||
case HostReplyMessage.MESSAGE_TYPE:
|
||||
return new HostReplyMessage();
|
||||
case CreateLeaseSet2Message.MESSAGE_TYPE:
|
||||
return new CreateLeaseSet2Message();
|
||||
default:
|
||||
throw new I2CPMessageException("The type " + type + " is an unknown I2CP message");
|
||||
}
|
||||
|
Reference in New Issue
Block a user