forked from I2P_Developers/i2p.i2p
NetDB: Add support for database lookup ratchet replies (proposal 154)
Add support for ElG-encrypted database lookups and stores from ECIES-only destinations Add ratchet support to MessageWrapper Application-specific timeout for MessageWrapper-generated tags Refactor tunnel TestJob to use MessageWrapper Add ratchet support to TestJob TestJob cleanups
This commit is contained in:
21
history.txt
21
history.txt
@ -1,4 +1,25 @@
|
|||||||
|
2020-03-31 zzz
|
||||||
|
* NetDB:
|
||||||
|
- Add support for ratchet replies (proposal 154)
|
||||||
|
- Add support for ElG lookups and stores from ECIES-only destinations
|
||||||
|
- Variable timeout for MessageWrapper-generated tags
|
||||||
|
* Ratchet:
|
||||||
|
- Variable timeout for tagsets
|
||||||
|
- Expire tags too far behind current one
|
||||||
|
- Remove ID and DI from ACKREQ block
|
||||||
|
- Add timeout job in OCMOSJ
|
||||||
|
- Prep for next key support
|
||||||
|
- Add support for acks and callbacks
|
||||||
|
* Tunnels:
|
||||||
|
- Refactor TestJob to use MessageWrapper
|
||||||
|
- Add support for ratchet
|
||||||
|
|
||||||
2020-03-24 zzz
|
2020-03-24 zzz
|
||||||
|
* Blockfile: Add b32 to export output
|
||||||
|
* Graphs: Fix rrd4j deprecation warnings
|
||||||
|
* Profiles:
|
||||||
|
- Don't decay during first 90 minutes of uptime
|
||||||
|
- Change decay from .75 twice a day to .84 four times a day
|
||||||
* Tunnels: Make new tunnel selection round-robin
|
* Tunnels: Make new tunnel selection round-robin
|
||||||
|
|
||||||
2020-03-20 zzz
|
2020-03-20 zzz
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 4;
|
public final static long BUILD = 5;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
@ -232,7 +232,7 @@ class OutboundClientMessageJobHelper {
|
|||||||
LeaseSetKeys lsk = ctx.keyManager().getKeys(from);
|
LeaseSetKeys lsk = ctx.keyManager().getKeys(from);
|
||||||
I2NPMessage msg;
|
I2NPMessage msg;
|
||||||
if (lsk == null || lsk.isSupported(EncType.ELGAMAL_2048)) {
|
if (lsk == null || lsk.isSupported(EncType.ELGAMAL_2048)) {
|
||||||
msg = wrapDSM(ctx, skm, dsm);
|
msg = wrapDSM(ctx, skm, dsm, expiration);
|
||||||
if (msg == null) {
|
if (msg == null) {
|
||||||
if (log.shouldLog(Log.WARN))
|
if (log.shouldLog(Log.WARN))
|
||||||
log.warn("Failed to wrap ack clove");
|
log.warn("Failed to wrap ack clove");
|
||||||
@ -279,12 +279,14 @@ class OutboundClientMessageJobHelper {
|
|||||||
* @return null on error
|
* @return null on error
|
||||||
* @since 0.9.12
|
* @since 0.9.12
|
||||||
*/
|
*/
|
||||||
private static GarlicMessage wrapDSM(RouterContext ctx, SessionKeyManager skm, DeliveryStatusMessage dsm) {
|
private static GarlicMessage wrapDSM(RouterContext ctx, SessionKeyManager skm,
|
||||||
|
DeliveryStatusMessage dsm, long expiration) {
|
||||||
// garlic route that DeliveryStatusMessage to ourselves so the endpoints and gateways
|
// garlic route that DeliveryStatusMessage to ourselves so the endpoints and gateways
|
||||||
// can't tell its a test. to simplify this, we encrypt it with a random key and tag,
|
// can't tell its a test. to simplify this, we encrypt it with a random key and tag,
|
||||||
// remembering that key+tag so that we can decrypt it later. this means we can do the
|
// remembering that key+tag so that we can decrypt it later. this means we can do the
|
||||||
// garlic encryption without any ElGamal (yay)
|
// garlic encryption without any ElGamal (yay)
|
||||||
MessageWrapper.OneTimeSession sess = MessageWrapper.generateSession(ctx, skm);
|
long fromNow = expiration - ctx.clock().now();
|
||||||
|
MessageWrapper.OneTimeSession sess = MessageWrapper.generateSession(ctx, skm, fromNow, true);
|
||||||
GarlicMessage msg = MessageWrapper.wrap(ctx, dsm, sess);
|
GarlicMessage msg = MessageWrapper.wrap(ctx, dsm, sess);
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import net.i2p.data.LeaseSet;
|
|||||||
import net.i2p.data.router.RouterIdentity;
|
import net.i2p.data.router.RouterIdentity;
|
||||||
import net.i2p.data.router.RouterInfo;
|
import net.i2p.data.router.RouterInfo;
|
||||||
import net.i2p.data.SessionKey;
|
import net.i2p.data.SessionKey;
|
||||||
|
import net.i2p.data.SessionTag;
|
||||||
import net.i2p.data.TunnelId;
|
import net.i2p.data.TunnelId;
|
||||||
import net.i2p.data.i2np.DatabaseLookupMessage;
|
import net.i2p.data.i2np.DatabaseLookupMessage;
|
||||||
import net.i2p.data.i2np.DatabaseSearchReplyMessage;
|
import net.i2p.data.i2np.DatabaseSearchReplyMessage;
|
||||||
@ -29,6 +30,7 @@ import net.i2p.router.JobImpl;
|
|||||||
import net.i2p.router.OutNetMessage;
|
import net.i2p.router.OutNetMessage;
|
||||||
import net.i2p.router.Router;
|
import net.i2p.router.Router;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
|
import net.i2p.router.crypto.ratchet.RatchetSessionTag;
|
||||||
import net.i2p.router.networkdb.kademlia.MessageWrapper;
|
import net.i2p.router.networkdb.kademlia.MessageWrapper;
|
||||||
import net.i2p.router.message.SendMessageDirectJob;
|
import net.i2p.router.message.SendMessageDirectJob;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
@ -313,11 +315,19 @@ public class HandleDatabaseLookupMessageJob extends JobImpl {
|
|||||||
SessionKey replyKey = _message.getReplyKey();
|
SessionKey replyKey = _message.getReplyKey();
|
||||||
if (replyKey != null) {
|
if (replyKey != null) {
|
||||||
// encrypt the reply
|
// encrypt the reply
|
||||||
if (_log.shouldLog(Log.INFO))
|
SessionTag tag = _message.getReplyTag();
|
||||||
_log.info("Sending encrypted reply to " + toPeer + ' ' + replyKey + ' ' + _message.getReplyTag());
|
if (tag != null) {
|
||||||
message = MessageWrapper.wrap(getContext(), message, replyKey, _message.getReplyTag());
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Sending AES reply to " + toPeer + ' ' + replyKey + ' ' + tag);
|
||||||
|
message = MessageWrapper.wrap(getContext(), message, replyKey, tag);
|
||||||
|
} else {
|
||||||
|
RatchetSessionTag rtag = _message.getRatchetReplyTag();
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Sending AEAD reply to " + toPeer + ' ' + replyKey + ' ' + rtag);
|
||||||
|
message = MessageWrapper.wrap(getContext(), message, replyKey, rtag);
|
||||||
|
}
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
_log.error("Encryption error");
|
_log.error("DLM reply encryption error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_replyKeyConsumed = true;
|
_replyKeyConsumed = true;
|
||||||
|
@ -141,7 +141,7 @@ class ExploreJob extends SearchJob {
|
|||||||
// request encrypted reply?
|
// request encrypted reply?
|
||||||
if (DatabaseLookupMessage.supportsEncryptedReplies(peer)) {
|
if (DatabaseLookupMessage.supportsEncryptedReplies(peer)) {
|
||||||
MessageWrapper.OneTimeSession sess;
|
MessageWrapper.OneTimeSession sess;
|
||||||
sess = MessageWrapper.generateSession(getContext());
|
sess = MessageWrapper.generateSession(getContext(), MAX_EXPLORE_TIME);
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info(getJobId() + ": Requesting encrypted reply from " + peer.getIdentity().calculateHash() +
|
_log.info(getJobId() + ": Requesting encrypted reply from " + peer.getIdentity().calculateHash() +
|
||||||
' ' + sess.key + ' ' + sess.tag);
|
' ' + sess.key + ' ' + sess.tag);
|
||||||
|
@ -146,16 +146,23 @@ class FloodfillVerifyStoreJob extends JobImpl {
|
|||||||
_facade.verifyFinished(_key);
|
_facade.verifyFinished(_key);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
boolean supportsElGamal = true;
|
||||||
|
boolean supportsRatchet = false;
|
||||||
if (DatabaseLookupMessage.supportsEncryptedReplies(peer)) {
|
if (DatabaseLookupMessage.supportsEncryptedReplies(peer)) {
|
||||||
// register the session with the right SKM
|
// register the session with the right SKM
|
||||||
MessageWrapper.OneTimeSession sess;
|
MessageWrapper.OneTimeSession sess;
|
||||||
if (isInboundExploratory) {
|
if (isInboundExploratory) {
|
||||||
sess = MessageWrapper.generateSession(getContext());
|
sess = MessageWrapper.generateSession(getContext(), VERIFY_TIMEOUT);
|
||||||
} else {
|
} else {
|
||||||
LeaseSetKeys lsk = getContext().keyManager().getKeys(_client);
|
LeaseSetKeys lsk = getContext().keyManager().getKeys(_client);
|
||||||
if (lsk == null || lsk.isSupported(EncType.ELGAMAL_2048)) {
|
supportsRatchet = lsk != null &&
|
||||||
|
lsk.isSupported(EncType.ECIES_X25519) &&
|
||||||
|
DatabaseLookupMessage.supportsRatchetReplies(peer);
|
||||||
|
supportsElGamal = lsk != null &&
|
||||||
|
lsk.isSupported(EncType.ELGAMAL_2048);
|
||||||
|
if (supportsElGamal || supportsRatchet) {
|
||||||
// garlic encrypt
|
// garlic encrypt
|
||||||
sess = MessageWrapper.generateSession(getContext(), _client);
|
sess = MessageWrapper.generateSession(getContext(), _client, VERIFY_TIMEOUT, !supportsRatchet);
|
||||||
if (sess == null) {
|
if (sess == null) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("No SKM to reply to");
|
_log.warn("No SKM to reply to");
|
||||||
@ -163,7 +170,7 @@ class FloodfillVerifyStoreJob extends JobImpl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We don't yet have any way to request/get a ECIES-tagged reply,
|
// We don't have a compatible way to get a reply,
|
||||||
// skip it for now.
|
// skip it for now.
|
||||||
if (_log.shouldWarn())
|
if (_log.shouldWarn())
|
||||||
_log.warn("Skipping store verify for ECIES client " + _client.toBase32());
|
_log.warn("Skipping store verify for ECIES client " + _client.toBase32());
|
||||||
@ -171,23 +178,41 @@ class FloodfillVerifyStoreJob extends JobImpl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (sess.tag != null) {
|
||||||
_log.info(getJobId() + ": Requesting encrypted reply from " + _target + ' ' + sess.key + ' ' + sess.tag);
|
if (_log.shouldInfo())
|
||||||
lookup.setReplySession(sess.key, sess.tag);
|
_log.info(getJobId() + ": Requesting AES reply from " + peer + ' ' + sess.key + ' ' + sess.tag);
|
||||||
|
lookup.setReplySession(sess.key, sess.tag);
|
||||||
|
} else {
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info(getJobId() + ": Requesting AEAD reply from " + peer + ' ' + sess.key + ' ' + sess.rtag);
|
||||||
|
lookup.setReplySession(sess.key, sess.rtag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Hash fromKey;
|
Hash fromKey;
|
||||||
if (_isRouterInfo)
|
I2NPMessage sent;
|
||||||
fromKey = null;
|
if (supportsElGamal) {
|
||||||
else
|
if (_isRouterInfo)
|
||||||
fromKey = _client;
|
fromKey = null;
|
||||||
_wrappedMessage = MessageWrapper.wrap(getContext(), lookup, fromKey, peer);
|
else
|
||||||
if (_wrappedMessage == null) {
|
fromKey = _client;
|
||||||
if (_log.shouldLog(Log.WARN))
|
_wrappedMessage = MessageWrapper.wrap(getContext(), lookup, fromKey, peer);
|
||||||
_log.warn("Fail Garlic encrypting");
|
if (_wrappedMessage == null) {
|
||||||
_facade.verifyFinished(_key);
|
if (_log.shouldLog(Log.WARN))
|
||||||
return;
|
_log.warn("Fail Garlic encrypting");
|
||||||
|
_facade.verifyFinished(_key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sent = _wrappedMessage.getMessage();
|
||||||
|
} else {
|
||||||
|
// force full ElG for ECIES fromkey
|
||||||
|
sent = MessageWrapper.wrap(getContext(), lookup, peer);
|
||||||
|
if (sent == null) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Fail Garlic encrypting");
|
||||||
|
_facade.verifyFinished(_key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
I2NPMessage sent = _wrappedMessage.getMessage();
|
|
||||||
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info(getJobId() + ": Starting verify (stored " + _key + " to " + _sentTo + "), asking " + _target);
|
_log.info(getJobId() + ": Starting verify (stored " + _key + " to " + _sentTo + "), asking " + _target);
|
||||||
|
@ -336,19 +336,27 @@ public class IterativeSearchJob extends FloodSearchJob {
|
|||||||
TunnelInfo replyTunnel;
|
TunnelInfo replyTunnel;
|
||||||
boolean isClientReplyTunnel;
|
boolean isClientReplyTunnel;
|
||||||
boolean isDirect;
|
boolean isDirect;
|
||||||
|
boolean supportsRatchet = false;
|
||||||
|
boolean supportsElGamal = true;
|
||||||
if (_fromLocalDest != null) {
|
if (_fromLocalDest != null) {
|
||||||
outTunnel = tm.selectOutboundTunnel(_fromLocalDest, peer);
|
outTunnel = tm.selectOutboundTunnel(_fromLocalDest, peer);
|
||||||
if (outTunnel == null)
|
if (outTunnel == null)
|
||||||
outTunnel = tm.selectOutboundExploratoryTunnel(peer);
|
outTunnel = tm.selectOutboundExploratoryTunnel(peer);
|
||||||
LeaseSetKeys lsk = getContext().keyManager().getKeys(_fromLocalDest);
|
LeaseSetKeys lsk = getContext().keyManager().getKeys(_fromLocalDest);
|
||||||
if (lsk == null || lsk.isSupported(EncType.ELGAMAL_2048)) {
|
supportsRatchet = lsk != null &&
|
||||||
|
lsk.isSupported(EncType.ECIES_X25519) &&
|
||||||
|
DatabaseLookupMessage.supportsRatchetReplies(ri);
|
||||||
|
supportsElGamal = !supportsRatchet &&
|
||||||
|
lsk != null &&
|
||||||
|
lsk.isSupported(EncType.ELGAMAL_2048);
|
||||||
|
if (supportsElGamal || supportsRatchet) {
|
||||||
// garlic encrypt to dest SKM
|
// garlic encrypt to dest SKM
|
||||||
replyTunnel = tm.selectInboundTunnel(_fromLocalDest, peer);
|
replyTunnel = tm.selectInboundTunnel(_fromLocalDest, peer);
|
||||||
isClientReplyTunnel = replyTunnel != null;
|
isClientReplyTunnel = replyTunnel != null;
|
||||||
if (!isClientReplyTunnel)
|
if (!isClientReplyTunnel)
|
||||||
replyTunnel = tm.selectInboundExploratoryTunnel(peer);
|
replyTunnel = tm.selectInboundExploratoryTunnel(peer);
|
||||||
} else {
|
} else {
|
||||||
// We don't yet have any way to request/get a ECIES-tagged reply,
|
// We don't have a way to request/get a ECIES-tagged reply,
|
||||||
// so send it to the router SKM
|
// so send it to the router SKM
|
||||||
isClientReplyTunnel = false;
|
isClientReplyTunnel = false;
|
||||||
replyTunnel = tm.selectInboundExploratoryTunnel(peer);
|
replyTunnel = tm.selectInboundExploratoryTunnel(peer);
|
||||||
@ -443,18 +451,24 @@ public class IterativeSearchJob extends FloodSearchJob {
|
|||||||
_log.warn(getJobId() + ": Can't do encrypted lookup to " + peer + " with EncType " + type);
|
_log.warn(getJobId() + ": Can't do encrypted lookup to " + peer + " with EncType " + type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (true) {
|
|
||||||
MessageWrapper.OneTimeSession sess;
|
MessageWrapper.OneTimeSession sess;
|
||||||
if (isClientReplyTunnel)
|
if (isClientReplyTunnel)
|
||||||
sess = MessageWrapper.generateSession(getContext(), _fromLocalDest);
|
sess = MessageWrapper.generateSession(getContext(), _fromLocalDest, SINGLE_SEARCH_MSG_TIME, !supportsRatchet);
|
||||||
else
|
else
|
||||||
sess = MessageWrapper.generateSession(getContext());
|
sess = MessageWrapper.generateSession(getContext(), SINGLE_SEARCH_MSG_TIME);
|
||||||
if (sess != null) {
|
if (sess != null) {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (sess.tag != null) {
|
||||||
_log.info(getJobId() + ": Requesting encrypted reply from " + peer + ' ' + sess.key + ' ' + sess.tag);
|
if (_log.shouldInfo())
|
||||||
|
_log.info(getJobId() + ": Requesting AES reply from " + peer + ' ' + sess.key + ' ' + sess.tag);
|
||||||
dlm.setReplySession(sess.key, sess.tag);
|
dlm.setReplySession(sess.key, sess.tag);
|
||||||
} // else client went away, but send it anyway
|
} else {
|
||||||
}
|
if (_log.shouldInfo())
|
||||||
|
_log.info(getJobId() + ": Requesting AEAD reply from " + peer + ' ' + sess.key + ' ' + sess.rtag);
|
||||||
|
dlm.setReplySession(sess.key, sess.rtag);
|
||||||
|
}
|
||||||
|
} // else client went away, but send it anyway
|
||||||
|
|
||||||
outMsg = MessageWrapper.wrap(getContext(), dlm, ri);
|
outMsg = MessageWrapper.wrap(getContext(), dlm, ri);
|
||||||
// ElG can take a while so do a final check before we send it,
|
// ElG can take a while so do a final check before we send it,
|
||||||
// a response may have come in.
|
// a response may have come in.
|
||||||
|
@ -9,13 +9,17 @@ import net.i2p.crypto.TagSetHandle;
|
|||||||
import net.i2p.data.Certificate;
|
import net.i2p.data.Certificate;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.PublicKey;
|
import net.i2p.data.PublicKey;
|
||||||
import net.i2p.data.router.RouterInfo;
|
|
||||||
import net.i2p.data.SessionKey;
|
import net.i2p.data.SessionKey;
|
||||||
import net.i2p.data.SessionTag;
|
import net.i2p.data.SessionTag;
|
||||||
import net.i2p.data.i2np.DeliveryInstructions;
|
import net.i2p.data.i2np.DeliveryInstructions;
|
||||||
import net.i2p.data.i2np.GarlicMessage;
|
import net.i2p.data.i2np.GarlicMessage;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
|
import net.i2p.data.router.RouterInfo;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
|
import net.i2p.router.crypto.TransientSessionKeyManager;
|
||||||
|
import net.i2p.router.crypto.ratchet.MuxedSKM;
|
||||||
|
import net.i2p.router.crypto.ratchet.RatchetSKM;
|
||||||
|
import net.i2p.router.crypto.ratchet.RatchetSessionTag;
|
||||||
import net.i2p.router.message.GarlicMessageBuilder;
|
import net.i2p.router.message.GarlicMessageBuilder;
|
||||||
import net.i2p.router.message.PayloadGarlicConfig;
|
import net.i2p.router.message.PayloadGarlicConfig;
|
||||||
import net.i2p.router.util.RemovableSingletonSet;
|
import net.i2p.router.util.RemovableSingletonSet;
|
||||||
@ -63,7 +67,7 @@ public class MessageWrapper {
|
|||||||
if (skm == null)
|
if (skm == null)
|
||||||
return null;
|
return null;
|
||||||
SessionKey sentKey = new SessionKey();
|
SessionKey sentKey = new SessionKey();
|
||||||
Set<SessionTag> sentTags = new HashSet<SessionTag>();
|
Set<SessionTag> sentTags = new HashSet<SessionTag>(NETDB_TAGS_TO_DELIVER);
|
||||||
GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, payload, sentKey, sentTags,
|
GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, payload, sentKey, sentTags,
|
||||||
NETDB_TAGS_TO_DELIVER, NETDB_LOW_THRESHOLD, skm);
|
NETDB_TAGS_TO_DELIVER, NETDB_LOW_THRESHOLD, skm);
|
||||||
if (msg == null)
|
if (msg == null)
|
||||||
@ -150,11 +154,25 @@ public class MessageWrapper {
|
|||||||
* @since 0.9.7
|
* @since 0.9.7
|
||||||
*/
|
*/
|
||||||
public static class OneTimeSession {
|
public static class OneTimeSession {
|
||||||
|
/** ElG or ratchet */
|
||||||
public final SessionKey key;
|
public final SessionKey key;
|
||||||
|
/** non-null for ElG */
|
||||||
public final SessionTag tag;
|
public final SessionTag tag;
|
||||||
|
/**
|
||||||
|
* non-null for ratchet
|
||||||
|
* @since 0.9.46
|
||||||
|
*/
|
||||||
|
public final RatchetSessionTag rtag;
|
||||||
|
|
||||||
public OneTimeSession(SessionKey key, SessionTag tag) {
|
public OneTimeSession(SessionKey key, SessionTag tag) {
|
||||||
this.key = key; this.tag = tag;
|
this.key = key; this.tag = tag;
|
||||||
|
rtag = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.46 */
|
||||||
|
public OneTimeSession(SessionKey key, RatchetSessionTag tag) {
|
||||||
|
this.key = key; rtag = tag;
|
||||||
|
this.tag = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,10 +182,11 @@ public class MessageWrapper {
|
|||||||
* The recipient can then send us an AES-encrypted message,
|
* The recipient can then send us an AES-encrypted message,
|
||||||
* avoiding ElGamal.
|
* avoiding ElGamal.
|
||||||
*
|
*
|
||||||
|
* @param expiration time from now
|
||||||
* @since 0.9.7
|
* @since 0.9.7
|
||||||
*/
|
*/
|
||||||
public static OneTimeSession generateSession(RouterContext ctx) {
|
public static OneTimeSession generateSession(RouterContext ctx, long expiration) {
|
||||||
return generateSession(ctx, ctx.sessionKeyManager());
|
return generateSession(ctx, ctx.sessionKeyManager(), expiration, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -176,14 +195,16 @@ public class MessageWrapper {
|
|||||||
* The recipient can then send us an AES-encrypted message,
|
* The recipient can then send us an AES-encrypted message,
|
||||||
* avoiding ElGamal.
|
* avoiding ElGamal.
|
||||||
*
|
*
|
||||||
|
* @param expiration time from now
|
||||||
* @return null if we can't find the SKM for the localDest
|
* @return null if we can't find the SKM for the localDest
|
||||||
* @since 0.9.9
|
* @since 0.9.9
|
||||||
*/
|
*/
|
||||||
public static OneTimeSession generateSession(RouterContext ctx, Hash localDest) {
|
public static OneTimeSession generateSession(RouterContext ctx, Hash localDest,
|
||||||
|
long expiration, boolean forceElG) {
|
||||||
SessionKeyManager skm = ctx.clientManager().getClientSessionKeyManager(localDest);
|
SessionKeyManager skm = ctx.clientManager().getClientSessionKeyManager(localDest);
|
||||||
if (skm == null)
|
if (skm == null)
|
||||||
return null;
|
return null;
|
||||||
return generateSession(ctx, skm);
|
return generateSession(ctx, skm, expiration, forceElG);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -192,29 +213,49 @@ public class MessageWrapper {
|
|||||||
* The recipient can then send us an AES-encrypted message,
|
* The recipient can then send us an AES-encrypted message,
|
||||||
* avoiding ElGamal.
|
* avoiding ElGamal.
|
||||||
*
|
*
|
||||||
|
* @param expiration time from now
|
||||||
* @return non-null
|
* @return non-null
|
||||||
* @since 0.9.9
|
* @since 0.9.9
|
||||||
*/
|
*/
|
||||||
public static OneTimeSession generateSession(RouterContext ctx, SessionKeyManager skm) {
|
public static OneTimeSession generateSession(RouterContext ctx, SessionKeyManager skm,
|
||||||
|
long expiration, boolean forceElG) {
|
||||||
SessionKey key = ctx.keyGenerator().generateSessionKey();
|
SessionKey key = ctx.keyGenerator().generateSessionKey();
|
||||||
SessionTag tag = new SessionTag(true);
|
if (forceElG || (skm instanceof TransientSessionKeyManager)) {
|
||||||
Set<SessionTag> tags = new RemovableSingletonSet<SessionTag>(tag);
|
SessionTag tag = new SessionTag(true);
|
||||||
skm.tagsReceived(key, tags, 2*60*1000);
|
Set<SessionTag> tags = new RemovableSingletonSet<SessionTag>(tag);
|
||||||
|
skm.tagsReceived(key, tags, expiration);
|
||||||
|
return new OneTimeSession(key, tag);
|
||||||
|
}
|
||||||
|
// ratchet
|
||||||
|
RatchetSKM rskm;
|
||||||
|
if (skm instanceof RatchetSKM) {
|
||||||
|
rskm = (RatchetSKM) skm;
|
||||||
|
} else if (skm instanceof MuxedSKM) {
|
||||||
|
rskm = ((MuxedSKM) skm).getECSKM();
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("skm not a ratchet " + skm);
|
||||||
|
}
|
||||||
|
RatchetSessionTag tag = new RatchetSessionTag(ctx.random().nextLong());
|
||||||
|
rskm.tagsReceived(key, tag, expiration);
|
||||||
return new OneTimeSession(key, tag);
|
return new OneTimeSession(key, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Garlic wrap a message from nobody, destined for an unknown router,
|
* Garlic wrap a message from nobody, destined for an unknown router,
|
||||||
* to hide the contents from the IBGW.
|
* to hide the contents from the IBGW.
|
||||||
* Uses a supplied one-time session key tag for AES encryption,
|
* Uses a supplied one-time session key tag for AES or AEAD encryption,
|
||||||
* avoiding ElGamal.
|
* avoiding ElGamal or X25519.
|
||||||
|
*
|
||||||
|
* Used by OCMJH for DSM.
|
||||||
*
|
*
|
||||||
* @param session non-null
|
* @param session non-null
|
||||||
* @return null on encrypt failure
|
* @return null on encrypt failure
|
||||||
* @since 0.9.12
|
* @since 0.9.12
|
||||||
*/
|
*/
|
||||||
public static GarlicMessage wrap(RouterContext ctx, I2NPMessage m, OneTimeSession session) {
|
public static GarlicMessage wrap(RouterContext ctx, I2NPMessage m, OneTimeSession session) {
|
||||||
return wrap(ctx, m, session.key, session.tag);
|
if (session.tag != null)
|
||||||
|
return wrap(ctx, m, session.key, session.tag);
|
||||||
|
return wrap(ctx, m, session.key, session.rtag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,6 +264,8 @@ public class MessageWrapper {
|
|||||||
* Uses a supplied session key and session tag for AES encryption,
|
* Uses a supplied session key and session tag for AES encryption,
|
||||||
* avoiding ElGamal.
|
* avoiding ElGamal.
|
||||||
*
|
*
|
||||||
|
* Used by above and for DLM replies in HDLMJ.
|
||||||
|
*
|
||||||
* @param encryptKey non-null
|
* @param encryptKey non-null
|
||||||
* @param encryptTag non-null
|
* @param encryptTag non-null
|
||||||
* @return null on encrypt failure
|
* @return null on encrypt failure
|
||||||
@ -233,9 +276,30 @@ public class MessageWrapper {
|
|||||||
ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE),
|
ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE),
|
||||||
m.getMessageExpiration(),
|
m.getMessageExpiration(),
|
||||||
DeliveryInstructions.LOCAL, m);
|
DeliveryInstructions.LOCAL, m);
|
||||||
|
|
||||||
GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, payload, null,
|
GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, payload, null,
|
||||||
null, encryptKey, encryptTag);
|
null, encryptKey, encryptTag);
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Garlic wrap a message from nobody, destined for an unknown router,
|
||||||
|
* to hide the contents from the IBGW.
|
||||||
|
* Uses a supplied session key and session tag for ratchet encryption,
|
||||||
|
* avoiding full ECIES.
|
||||||
|
*
|
||||||
|
* Used by above and for DLM replies in HDLMJ.
|
||||||
|
*
|
||||||
|
* @param encryptKey non-null
|
||||||
|
* @param encryptTag non-null
|
||||||
|
* @return null on encrypt failure
|
||||||
|
* @since 0.9.46
|
||||||
|
*/
|
||||||
|
public static GarlicMessage wrap(RouterContext ctx, I2NPMessage m, SessionKey encryptKey, RatchetSessionTag encryptTag) {
|
||||||
|
PayloadGarlicConfig payload = new PayloadGarlicConfig(Certificate.NULL_CERT,
|
||||||
|
ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE),
|
||||||
|
m.getMessageExpiration(),
|
||||||
|
DeliveryInstructions.LOCAL, m);
|
||||||
|
GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, payload, encryptKey, encryptTag);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
import net.i2p.crypto.EncType;
|
import net.i2p.crypto.EncType;
|
||||||
import net.i2p.crypto.SigType;
|
import net.i2p.crypto.SigType;
|
||||||
|
import net.i2p.data.Base64;
|
||||||
import net.i2p.data.Certificate;
|
import net.i2p.data.Certificate;
|
||||||
import net.i2p.data.DatabaseEntry;
|
import net.i2p.data.DatabaseEntry;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
@ -297,7 +298,8 @@ abstract class StoreJob extends JobImpl {
|
|||||||
Hash rkey = getContext().routingKeyGenerator().getRoutingKey(key);
|
Hash rkey = getContext().routingKeyGenerator().getRoutingKey(key);
|
||||||
KBucketSet<Hash> ks = _facade.getKBuckets();
|
KBucketSet<Hash> ks = _facade.getKBuckets();
|
||||||
if (ks == null) return new ArrayList<Hash>();
|
if (ks == null) return new ArrayList<Hash>();
|
||||||
return ((FloodfillPeerSelector)_peerSelector).selectFloodfillParticipants(rkey, numClosest, alreadyChecked, ks);
|
List<Hash> rv = ((FloodfillPeerSelector)_peerSelector).selectFloodfillParticipants(rkey, numClosest, alreadyChecked, ks);
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** limit expiration for direct sends */
|
/** limit expiration for direct sends */
|
||||||
@ -496,9 +498,19 @@ abstract class StoreJob extends JobImpl {
|
|||||||
}
|
}
|
||||||
sent = wm.getMessage();
|
sent = wm.getMessage();
|
||||||
_state.addPending(to, wm);
|
_state.addPending(to, wm);
|
||||||
|
} else if (lsk.isSupported(EncType.ECIES_X25519)) {
|
||||||
|
// force full ElG for ECIES-only
|
||||||
|
sent = MessageWrapper.wrap(getContext(), msg, peer);
|
||||||
|
if (sent == null) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Fail garlic encrypting from: " + client);
|
||||||
|
fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_state.addPending(to);
|
||||||
} else {
|
} else {
|
||||||
// We don't yet have any way to request/get a ECIES-tagged reply,
|
// Above are the only two enc types for now, won't get here.
|
||||||
// so send it unencrypted.
|
// Send it unencrypted.
|
||||||
sent = msg;
|
sent = msg;
|
||||||
_state.addPending(to);
|
_state.addPending(to);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package net.i2p.router.tunnel.pool;
|
package net.i2p.router.tunnel.pool;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import net.i2p.crypto.SessionKeyManager;
|
import net.i2p.crypto.SessionKeyManager;
|
||||||
import net.i2p.data.Certificate;
|
|
||||||
import net.i2p.data.SessionKey;
|
|
||||||
import net.i2p.data.SessionTag;
|
import net.i2p.data.SessionTag;
|
||||||
import net.i2p.data.i2np.DeliveryInstructions;
|
|
||||||
import net.i2p.data.i2np.DeliveryStatusMessage;
|
import net.i2p.data.i2np.DeliveryStatusMessage;
|
||||||
import net.i2p.data.i2np.GarlicMessage;
|
import net.i2p.data.i2np.GarlicMessage;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
@ -16,9 +13,7 @@ import net.i2p.router.OutNetMessage;
|
|||||||
import net.i2p.router.ReplyJob;
|
import net.i2p.router.ReplyJob;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.router.TunnelInfo;
|
import net.i2p.router.TunnelInfo;
|
||||||
import net.i2p.router.message.GarlicMessageBuilder;
|
import net.i2p.router.networkdb.kademlia.MessageWrapper;
|
||||||
import net.i2p.router.message.PayloadGarlicConfig;
|
|
||||||
import net.i2p.router.util.RemovableSingletonSet;
|
|
||||||
import net.i2p.stat.Rate;
|
import net.i2p.stat.Rate;
|
||||||
import net.i2p.stat.RateStat;
|
import net.i2p.stat.RateStat;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
@ -26,6 +21,9 @@ import net.i2p.util.Log;
|
|||||||
/**
|
/**
|
||||||
* Repeatedly test a single tunnel for its entire lifetime,
|
* Repeatedly test a single tunnel for its entire lifetime,
|
||||||
* or until the pool is shut down or removed from the client manager.
|
* or until the pool is shut down or removed from the client manager.
|
||||||
|
*
|
||||||
|
* Tunnel testing is disabled by default now, except for hidden mode,
|
||||||
|
* see TunnelPoolManager.buildComplete()
|
||||||
*/
|
*/
|
||||||
class TestJob extends JobImpl {
|
class TestJob extends JobImpl {
|
||||||
private final Log _log;
|
private final Log _log;
|
||||||
@ -37,6 +35,8 @@ class TestJob extends JobImpl {
|
|||||||
private PooledTunnelCreatorConfig _otherTunnel;
|
private PooledTunnelCreatorConfig _otherTunnel;
|
||||||
/** save this so we can tell the SKM to kill it if the test fails */
|
/** save this so we can tell the SKM to kill it if the test fails */
|
||||||
private SessionTag _encryptTag;
|
private SessionTag _encryptTag;
|
||||||
|
private static final AtomicInteger __id = new AtomicInteger();
|
||||||
|
private int _id;
|
||||||
|
|
||||||
/** base to randomize the test delay on */
|
/** base to randomize the test delay on */
|
||||||
private static final int TEST_DELAY = 40*1000;
|
private static final int TEST_DELAY = 40*1000;
|
||||||
@ -60,15 +60,16 @@ class TestJob extends JobImpl {
|
|||||||
public void runJob() {
|
public void runJob() {
|
||||||
if (_pool == null || !_pool.isAlive())
|
if (_pool == null || !_pool.isAlive())
|
||||||
return;
|
return;
|
||||||
long lag = getContext().jobQueue().getMaxLag();
|
final RouterContext ctx = getContext();
|
||||||
|
long lag = ctx.jobQueue().getMaxLag();
|
||||||
if (lag > 3000) {
|
if (lag > 3000) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Deferring test of " + _cfg + " due to job lag = " + lag);
|
_log.warn("Deferring test of " + _cfg + " due to job lag = " + lag);
|
||||||
getContext().statManager().addRateData("tunnel.testAborted", _cfg.getLength(), 0);
|
ctx.statManager().addRateData("tunnel.testAborted", _cfg.getLength());
|
||||||
scheduleRetest();
|
scheduleRetest();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (getContext().router().gracefulShutdownInProgress())
|
if (ctx.router().gracefulShutdownInProgress())
|
||||||
return; // don't reschedule
|
return; // don't reschedule
|
||||||
_found = false;
|
_found = false;
|
||||||
// note: testing with exploratory tunnels always, even if the tested tunnel
|
// note: testing with exploratory tunnels always, even if the tested tunnel
|
||||||
@ -80,11 +81,11 @@ class TestJob extends JobImpl {
|
|||||||
if (_cfg.isInbound()) {
|
if (_cfg.isInbound()) {
|
||||||
_replyTunnel = _cfg;
|
_replyTunnel = _cfg;
|
||||||
// TODO if testing is re-enabled, pick closest to far end
|
// TODO if testing is re-enabled, pick closest to far end
|
||||||
_outTunnel = getContext().tunnelManager().selectOutboundTunnel();
|
_outTunnel = ctx.tunnelManager().selectOutboundTunnel();
|
||||||
_otherTunnel = (PooledTunnelCreatorConfig) _outTunnel;
|
_otherTunnel = (PooledTunnelCreatorConfig) _outTunnel;
|
||||||
} else {
|
} else {
|
||||||
// TODO if testing is re-enabled, pick closest to far end
|
// TODO if testing is re-enabled, pick closest to far end
|
||||||
_replyTunnel = getContext().tunnelManager().selectInboundTunnel();
|
_replyTunnel = ctx.tunnelManager().selectInboundTunnel();
|
||||||
_outTunnel = _cfg;
|
_outTunnel = _cfg;
|
||||||
_otherTunnel = (PooledTunnelCreatorConfig) _replyTunnel;
|
_otherTunnel = (PooledTunnelCreatorConfig) _replyTunnel;
|
||||||
}
|
}
|
||||||
@ -92,61 +93,59 @@ class TestJob extends JobImpl {
|
|||||||
if ( (_replyTunnel == null) || (_outTunnel == null) ) {
|
if ( (_replyTunnel == null) || (_outTunnel == null) ) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Insufficient tunnels to test " + _cfg + " with: " + _replyTunnel + " / " + _outTunnel);
|
_log.warn("Insufficient tunnels to test " + _cfg + " with: " + _replyTunnel + " / " + _outTunnel);
|
||||||
getContext().statManager().addRateData("tunnel.testAborted", _cfg.getLength(), 0);
|
ctx.statManager().addRateData("tunnel.testAborted", _cfg.getLength());
|
||||||
scheduleRetest();
|
scheduleRetest();
|
||||||
} else {
|
} else {
|
||||||
int testPeriod = getTestPeriod();
|
int testPeriod = getTestPeriod();
|
||||||
long testExpiration = getContext().clock().now() + testPeriod;
|
long now = ctx.clock().now();
|
||||||
DeliveryStatusMessage m = new DeliveryStatusMessage(getContext());
|
long testExpiration = now + testPeriod;
|
||||||
m.setArrival(getContext().clock().now());
|
DeliveryStatusMessage m = new DeliveryStatusMessage(ctx);
|
||||||
|
m.setArrival(now);
|
||||||
m.setMessageExpiration(testExpiration);
|
m.setMessageExpiration(testExpiration);
|
||||||
m.setMessageId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
m.setMessageId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||||
ReplySelector sel = new ReplySelector(getContext(), m.getMessageId(), testExpiration);
|
ReplySelector sel = new ReplySelector(m.getMessageId(), testExpiration);
|
||||||
OnTestReply onReply = new OnTestReply(getContext());
|
OnTestReply onReply = new OnTestReply();
|
||||||
OnTestTimeout onTimeout = new OnTestTimeout(getContext());
|
OnTestTimeout onTimeout = new OnTestTimeout();
|
||||||
OutNetMessage msg = getContext().messageRegistry().registerPending(sel, onReply, onTimeout);
|
OutNetMessage msg = ctx.messageRegistry().registerPending(sel, onReply, onTimeout);
|
||||||
onReply.setSentMessage(msg);
|
onReply.setSentMessage(msg);
|
||||||
sendTest(m);
|
sendTest(m, testPeriod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendTest(I2NPMessage m) {
|
private void sendTest(I2NPMessage m, int testPeriod) {
|
||||||
// garlic route that DeliveryStatusMessage to ourselves so the endpoints and gateways
|
// garlic route that DeliveryStatusMessage to ourselves so the endpoints and gateways
|
||||||
// can't tell its a test. to simplify this, we encrypt it with a random key and tag,
|
// can't tell its a test. to simplify this, we encrypt it with a random key and tag,
|
||||||
// remembering that key+tag so that we can decrypt it later. this means we can do the
|
// remembering that key+tag so that we can decrypt it later. this means we can do the
|
||||||
// garlic encryption without any ElGamal (yay)
|
// garlic encryption without any ElGamal (yay)
|
||||||
PayloadGarlicConfig payload = new PayloadGarlicConfig(Certificate.NULL_CERT,
|
final RouterContext ctx = getContext();
|
||||||
getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE),
|
MessageWrapper.OneTimeSession sess;
|
||||||
m.getMessageExpiration(),
|
if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) {
|
||||||
DeliveryInstructions.LOCAL, m);
|
// to client. false means don't force AES
|
||||||
payload.setRecipient(getContext().router().getRouterInfo());
|
sess = MessageWrapper.generateSession(ctx, _pool.getSettings().getDestination(), testPeriod, false);
|
||||||
|
} else {
|
||||||
SessionKey encryptKey = getContext().keyGenerator().generateSessionKey();
|
// to router. AES.
|
||||||
SessionTag encryptTag = new SessionTag(true);
|
sess = MessageWrapper.generateSession(ctx, testPeriod);
|
||||||
_encryptTag = encryptTag;
|
}
|
||||||
Set<SessionTag> sentTags = null;
|
if (sess == null) {
|
||||||
GarlicMessage msg = GarlicMessageBuilder.buildMessage(getContext(), payload, sentTags,
|
scheduleRetest();
|
||||||
getContext().keyManager().getPublicKey(),
|
return;
|
||||||
encryptKey, encryptTag);
|
}
|
||||||
|
// null for ratchet
|
||||||
|
_encryptTag = sess.tag;
|
||||||
|
GarlicMessage msg;
|
||||||
|
if (sess.tag != null) // AES
|
||||||
|
msg = MessageWrapper.wrap(ctx, m, sess.key, sess.tag);
|
||||||
|
else // ratchet
|
||||||
|
msg = MessageWrapper.wrap(ctx, m, sess.key, sess.rtag);
|
||||||
if (msg == null) {
|
if (msg == null) {
|
||||||
// overloaded / unknown peers / etc
|
// overloaded / unknown peers / etc
|
||||||
scheduleRetest();
|
scheduleRetest();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Set<SessionTag> encryptTags = new RemovableSingletonSet<SessionTag>(encryptTag);
|
_id = __id.getAndIncrement();
|
||||||
// Register the single tag with the appropriate SKM
|
|
||||||
if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) {
|
|
||||||
SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_pool.getSettings().getDestination());
|
|
||||||
if (skm != null)
|
|
||||||
skm.tagsReceived(encryptKey, encryptTags);
|
|
||||||
} else {
|
|
||||||
getContext().sessionKeyManager().tagsReceived(encryptKey, encryptTags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Sending garlic test of " + _outTunnel + " / " + _replyTunnel);
|
_log.debug("Sending garlic test #" + _id + " of " + _outTunnel + " / " + _replyTunnel);
|
||||||
getContext().tunnelDispatcher().dispatchOutbound(msg, _outTunnel.getSendTunnelId(0),
|
ctx.tunnelDispatcher().dispatchOutbound(msg, _outTunnel.getSendTunnelId(0),
|
||||||
_replyTunnel.getReceiveTunnelId(0),
|
_replyTunnel.getReceiveTunnelId(0),
|
||||||
_replyTunnel.getPeer(0));
|
_replyTunnel.getPeer(0));
|
||||||
}
|
}
|
||||||
@ -154,8 +153,8 @@ class TestJob extends JobImpl {
|
|||||||
public void testSuccessful(int ms) {
|
public void testSuccessful(int ms) {
|
||||||
if (_pool == null || !_pool.isAlive())
|
if (_pool == null || !_pool.isAlive())
|
||||||
return;
|
return;
|
||||||
getContext().statManager().addRateData("tunnel.testSuccessLength", _cfg.getLength(), 0);
|
getContext().statManager().addRateData("tunnel.testSuccessLength", _cfg.getLength());
|
||||||
getContext().statManager().addRateData("tunnel.testSuccessTime", ms, 0);
|
getContext().statManager().addRateData("tunnel.testSuccessTime", ms);
|
||||||
|
|
||||||
_outTunnel.incrementVerifiedBytesTransferred(1024);
|
_outTunnel.incrementVerifiedBytesTransferred(1024);
|
||||||
// reply tunnel is marked in the inboundEndpointProcessor
|
// reply tunnel is marked in the inboundEndpointProcessor
|
||||||
@ -170,7 +169,7 @@ class TestJob extends JobImpl {
|
|||||||
_otherTunnel.testJobSuccessful(ms);
|
_otherTunnel.testJobSuccessful(ms);
|
||||||
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Tunnel test successful in " + ms + "ms: " + _cfg);
|
_log.debug("Tunnel test #" + _id + " successful in " + ms + "ms: " + _cfg);
|
||||||
scheduleRetest();
|
scheduleRetest();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +192,7 @@ class TestJob extends JobImpl {
|
|||||||
else
|
else
|
||||||
getContext().statManager().addRateData("tunnel.testFailedTime", timeToFail, timeToFail);
|
getContext().statManager().addRateData("tunnel.testFailedTime", timeToFail, timeToFail);
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Tunnel test failed in " + timeToFail + "ms: " + _cfg);
|
_log.warn("Tunnel test #" + _id + " failed in " + timeToFail + "ms: " + _cfg);
|
||||||
boolean keepGoing = _cfg.tunnelFailed();
|
boolean keepGoing = _cfg.tunnelFailed();
|
||||||
// blame the expl. tunnel too
|
// blame the expl. tunnel too
|
||||||
if (_otherTunnel.getLength() > 1)
|
if (_otherTunnel.getLength() > 1)
|
||||||
@ -234,6 +233,7 @@ class TestJob extends JobImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleRetest() { scheduleRetest(false); }
|
private void scheduleRetest() { scheduleRetest(false); }
|
||||||
|
|
||||||
private void scheduleRetest(boolean asap) {
|
private void scheduleRetest(boolean asap) {
|
||||||
if (_pool == null || !_pool.isAlive())
|
if (_pool == null || !_pool.isAlive())
|
||||||
return;
|
return;
|
||||||
@ -248,18 +248,16 @@ class TestJob extends JobImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class ReplySelector implements MessageSelector {
|
private class ReplySelector implements MessageSelector {
|
||||||
private final RouterContext _context;
|
|
||||||
private final long _id;
|
private final long _id;
|
||||||
private final long _expiration;
|
private final long _expiration;
|
||||||
|
|
||||||
public ReplySelector(RouterContext ctx, long id, long expiration) {
|
public ReplySelector(long id, long expiration) {
|
||||||
_context = ctx;
|
|
||||||
_id = id;
|
_id = id;
|
||||||
_expiration = expiration;
|
_expiration = expiration;
|
||||||
_found = false;
|
_found = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean continueMatching() { return !_found && _context.clock().now() < _expiration; }
|
public boolean continueMatching() { return !_found && getContext().clock().now() < _expiration; }
|
||||||
|
|
||||||
public long getExpiration() { return _expiration; }
|
public long getExpiration() { return _expiration; }
|
||||||
|
|
||||||
@ -286,7 +284,7 @@ class TestJob extends JobImpl {
|
|||||||
private long _successTime;
|
private long _successTime;
|
||||||
private OutNetMessage _sentMessage;
|
private OutNetMessage _sentMessage;
|
||||||
|
|
||||||
public OnTestReply(RouterContext ctx) { super(ctx); }
|
public OnTestReply() { super(TestJob.this.getContext()); }
|
||||||
|
|
||||||
public String getName() { return "Tunnel test success"; }
|
public String getName() { return "Tunnel test success"; }
|
||||||
|
|
||||||
@ -322,22 +320,23 @@ class TestJob extends JobImpl {
|
|||||||
private class OnTestTimeout extends JobImpl {
|
private class OnTestTimeout extends JobImpl {
|
||||||
private final long _started;
|
private final long _started;
|
||||||
|
|
||||||
public OnTestTimeout(RouterContext ctx) {
|
public OnTestTimeout() {
|
||||||
super(ctx);
|
super(TestJob.this.getContext());
|
||||||
_started = ctx.clock().now();
|
_started = getContext().clock().now();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() { return "Tunnel test timeout"; }
|
public String getName() { return "Tunnel test timeout"; }
|
||||||
|
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Timeout: found? " + _found);
|
_log.warn("Tunnel test #" + _id + " timeout: found? " + _found);
|
||||||
if (!_found) {
|
if (!_found) {
|
||||||
// don't clog up the SKM with old one-tag tagsets
|
// don't clog up the SKM with old one-tag tagsets
|
||||||
if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) {
|
if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) {
|
||||||
SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_pool.getSettings().getDestination());
|
SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_pool.getSettings().getDestination());
|
||||||
if (skm != null)
|
if (skm != null && _encryptTag != null)
|
||||||
skm.consumeTag(_encryptTag);
|
skm.consumeTag(_encryptTag);
|
||||||
|
// else null tag for ratchet, let it expire
|
||||||
} else {
|
} else {
|
||||||
getContext().sessionKeyManager().consumeTag(_encryptTag);
|
getContext().sessionKeyManager().consumeTag(_encryptTag);
|
||||||
}
|
}
|
||||||
|
@ -563,12 +563,8 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
|||||||
_context.router().isHidden() ||
|
_context.router().isHidden() ||
|
||||||
_context.router().getRouterInfo().getAddressCount() <= 0)) {
|
_context.router().getRouterInfo().getAddressCount() <= 0)) {
|
||||||
Hash client = cfg.getDestination();
|
Hash client = cfg.getDestination();
|
||||||
LeaseSetKeys lsk = client != null ? _context.keyManager().getKeys(client) : null;
|
TunnelPool pool = cfg.getTunnelPool();
|
||||||
if (lsk == null || lsk.isSupported(EncType.ELGAMAL_2048)) {
|
_context.jobQueue().addJob(new TestJob(_context, cfg, pool));
|
||||||
TunnelPool pool = cfg.getTunnelPool();
|
|
||||||
_context.jobQueue().addJob(new TestJob(_context, cfg, pool));
|
|
||||||
}
|
|
||||||
// else we don't yet have any way to request/get a ECIES-tagged reply,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user