forked from I2P_Developers/i2p.i2p
* Router: Encrypt DeliveryStatusMessages sent in garlics (ticket #1217)
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
2014-03-06 zzz
|
||||
* Router: Encrypt DeliveryStatusMessages sent in garlics (ticket #1217)
|
||||
|
||||
2014-03-05 zzz
|
||||
* Transports: Don't send a duplicate store of our RI at
|
||||
start of a connection (ticket #1187)
|
||||
|
@@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 13;
|
||||
public final static long BUILD = 14;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
@@ -28,13 +28,37 @@ import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelInfo;
|
||||
import net.i2p.router.networkdb.kademlia.MessageWrapper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Handle a particular client message that is destined for a remote destination.
|
||||
* Static methods to create a Garlic Message with one or more cloves, as follows:
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* GarlicMessage
|
||||
* Ack Clove (optional)
|
||||
* Tunnel delivery instructions
|
||||
* Garlic Message
|
||||
* Delivery Status Clove
|
||||
* Local delivery instructions
|
||||
* Delivery Status Message
|
||||
* LeaseSet Clove (optional)
|
||||
* Local delivery instructions
|
||||
* Database Store Message
|
||||
* Data Clove (required)
|
||||
* Destination delivery instructions
|
||||
* Data Message
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* The low-level construction is in GarlicMessageBuilder.
|
||||
*
|
||||
*/
|
||||
class OutboundClientMessageJobHelper {
|
||||
|
||||
private static final long ACK_EXTRA_EXPIRATION = 60*1000;
|
||||
|
||||
/**
|
||||
* Build a garlic message that will be delivered to the router on which the target is located.
|
||||
* Inside the message are two cloves: one containing the payload with instructions for
|
||||
@@ -54,8 +78,11 @@ class OutboundClientMessageJobHelper {
|
||||
* @param wrappedTags output parameter that will be filled with the sessionTags used
|
||||
* @param bundledReplyLeaseSet if specified, the given LeaseSet will be packaged with the message (allowing
|
||||
* much faster replies, since their netDb search will return almost instantly)
|
||||
* @param replyTunnel non-null if requireAck is true or bundledReplyLeaseSet is non-null
|
||||
* @param requireAck if true, bundle replyToken in an ack clove
|
||||
* @return garlic, or null if no tunnels were found (or other errors)
|
||||
*/
|
||||
/****
|
||||
static GarlicMessage createGarlicMessage(RouterContext ctx, long replyToken, long expiration, PublicKey recipientPK,
|
||||
Payload data, Hash from, Destination dest, TunnelInfo replyTunnel,
|
||||
SessionKey wrappedKey, Set<SessionTag> wrappedTags,
|
||||
@@ -64,6 +91,8 @@ class OutboundClientMessageJobHelper {
|
||||
return createGarlicMessage(ctx, replyToken, expiration, recipientPK, dataClove, from, dest, replyTunnel,
|
||||
0, 0, wrappedKey, wrappedTags, requireAck, bundledReplyLeaseSet);
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Allow the app to specify the data clove directly, which enables OutboundClientMessage to resend the
|
||||
* same payload (including expiration and unique id) in different garlics (down different tunnels)
|
||||
@@ -74,18 +103,23 @@ class OutboundClientMessageJobHelper {
|
||||
* @param lowTagsOverride if > 0, use this instead of skm's default
|
||||
* @param wrappedKey output parameter that will be filled with the sessionKey used
|
||||
* @param wrappedTags output parameter that will be filled with the sessionTags used
|
||||
* @param replyTunnel non-null if requireAck is true or bundledReplyLeaseSet is non-null
|
||||
* @param requireAck if true, bundle replyToken in an ack clove
|
||||
* @param bundledReplyLeaseSet may be null; if non-null, put it in a clove
|
||||
* @return garlic, or null if no tunnels were found (or other errors)
|
||||
*/
|
||||
static GarlicMessage createGarlicMessage(RouterContext ctx, long replyToken, long expiration, PublicKey recipientPK,
|
||||
PayloadGarlicConfig dataClove, Hash from, Destination dest, TunnelInfo replyTunnel,
|
||||
int tagsToSendOverride, int lowTagsOverride, SessionKey wrappedKey,
|
||||
Set<SessionTag> wrappedTags, boolean requireAck, LeaseSet bundledReplyLeaseSet) {
|
||||
GarlicConfig config = createGarlicConfig(ctx, replyToken, expiration, recipientPK, dataClove, from, dest, replyTunnel, requireAck, bundledReplyLeaseSet);
|
||||
if (config == null)
|
||||
return null;
|
||||
|
||||
SessionKeyManager skm = ctx.clientManager().getClientSessionKeyManager(from);
|
||||
if (skm == null)
|
||||
return null;
|
||||
GarlicConfig config = createGarlicConfig(ctx, replyToken, expiration, recipientPK, dataClove,
|
||||
from, dest, replyTunnel, requireAck, bundledReplyLeaseSet, skm);
|
||||
if (config == null)
|
||||
return null;
|
||||
// no use sending tags unless we have a reply token set up already
|
||||
int tagsToSend = replyToken >= 0 ? (tagsToSendOverride > 0 ? tagsToSendOverride : skm.getTagsToSend()) : 0;
|
||||
int lowThreshold = lowTagsOverride > 0 ? lowTagsOverride : skm.getLowThreshold();
|
||||
@@ -95,20 +129,31 @@ class OutboundClientMessageJobHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the top-level config, with a data clove, an optional ack clove, and
|
||||
* an optional leaseset clove.
|
||||
*
|
||||
* @param dataClove non-null
|
||||
* @param replyTunnel non-null if requireAck is true or bundledReplyLeaseSet is non-null
|
||||
* @param requireAck if true, bundle replyToken in an ack clove
|
||||
* @param bundledReplyLeaseSet may be null; if non-null, put it in a clove
|
||||
* @param skm encrypt dsm with this skm non-null
|
||||
* @return null on error
|
||||
*/
|
||||
private static GarlicConfig createGarlicConfig(RouterContext ctx, long replyToken, long expiration, PublicKey recipientPK,
|
||||
PayloadGarlicConfig dataClove, Hash from, Destination dest, TunnelInfo replyTunnel, boolean requireAck,
|
||||
LeaseSet bundledReplyLeaseSet) {
|
||||
PayloadGarlicConfig dataClove, Hash from, Destination dest,
|
||||
TunnelInfo replyTunnel, boolean requireAck,
|
||||
LeaseSet bundledReplyLeaseSet, SessionKeyManager skm) {
|
||||
Log log = ctx.logManager().getLog(OutboundClientMessageJobHelper.class);
|
||||
if (replyToken >= 0 && log.shouldLog(Log.DEBUG))
|
||||
log.debug("Reply token: " + replyToken);
|
||||
GarlicConfig config = new GarlicConfig();
|
||||
|
||||
if (requireAck) {
|
||||
PayloadGarlicConfig ackClove = buildAckClove(ctx, from, replyTunnel, replyToken, expiration);
|
||||
// extend the expiration of the return message
|
||||
PayloadGarlicConfig ackClove = buildAckClove(ctx, from, replyTunnel, replyToken,
|
||||
expiration + ACK_EXTRA_EXPIRATION, skm);
|
||||
if (ackClove == null)
|
||||
return null; // no tunnels
|
||||
return null; // no tunnels... TODO carry on anyway?
|
||||
config.addClove(ackClove);
|
||||
}
|
||||
|
||||
@@ -136,12 +181,15 @@ class OutboundClientMessageJobHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a clove that sends a DeliveryStatusMessage to us
|
||||
* @return null on error
|
||||
* Build a clove that sends a DeliveryStatusMessage to us.
|
||||
* As of 0.9.12, the DSM is wrapped in a GarlicMessage.
|
||||
* @param skm encrypt dsm with this skm non-null
|
||||
* @return null on error
|
||||
*/
|
||||
private static PayloadGarlicConfig buildAckClove(RouterContext ctx, Hash from, TunnelInfo replyToTunnel, long replyToken, long expiration) {
|
||||
private static PayloadGarlicConfig buildAckClove(RouterContext ctx, Hash from, TunnelInfo replyToTunnel,
|
||||
long replyToken, long expiration,
|
||||
SessionKeyManager skm) {
|
||||
Log log = ctx.logManager().getLog(OutboundClientMessageJobHelper.class);
|
||||
PayloadGarlicConfig ackClove = new PayloadGarlicConfig();
|
||||
|
||||
if (replyToTunnel == null) {
|
||||
if (log.shouldLog(Log.WARN))
|
||||
@@ -149,6 +197,7 @@ class OutboundClientMessageJobHelper {
|
||||
+ ", as there are no inbound tunnels available");
|
||||
return null;
|
||||
}
|
||||
|
||||
TunnelId replyToTunnelId = replyToTunnel.getReceiveTunnelId(0); // tunnel id on that gateway
|
||||
Hash replyToTunnelRouter = replyToTunnel.getPeer(0); // inbound tunnel gateway
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
@@ -164,18 +213,16 @@ class OutboundClientMessageJobHelper {
|
||||
//ackInstructions.setDelaySeconds(0);
|
||||
//ackInstructions.setEncrypted(false);
|
||||
|
||||
DeliveryStatusMessage msg = new DeliveryStatusMessage(ctx);
|
||||
msg.setArrival(ctx.clock().now());
|
||||
msg.setMessageId(replyToken);
|
||||
//if (log.shouldLog(Log.DEBUG))
|
||||
// log.debug("Delivery status message key: " + replyToken + " arrival: " + msg.getArrival());
|
||||
|
||||
PayloadGarlicConfig ackClove = new PayloadGarlicConfig();
|
||||
ackClove.setCertificate(Certificate.NULL_CERT);
|
||||
ackClove.setDeliveryInstructions(ackInstructions);
|
||||
ackClove.setExpiration(expiration);
|
||||
ackClove.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
DeliveryStatusMessage dsm = buildDSM(ctx, replyToken);
|
||||
GarlicMessage msg = wrapDSM(ctx, skm, dsm);
|
||||
ackClove.setPayload(msg);
|
||||
ackClove.setRecipient(ctx.router().getRouterInfo());
|
||||
// this does nothing, the clove is not separately encrypted
|
||||
//ackClove.setRecipient(ctx.router().getRouterInfo());
|
||||
// defaults
|
||||
//ackClove.setRequestAck(false);
|
||||
|
||||
@@ -188,11 +235,41 @@ class OutboundClientMessageJobHelper {
|
||||
return ackClove;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a basic DSM
|
||||
* @since 0.9.12
|
||||
*/
|
||||
private static DeliveryStatusMessage buildDSM(RouterContext ctx, long replyToken) {
|
||||
DeliveryStatusMessage msg = new DeliveryStatusMessage(ctx);
|
||||
msg.setArrival(ctx.clock().now());
|
||||
msg.setMessageId(replyToken);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* As of 0.9.12, encrypt to hide it from the target and the return path OBEP and IBGW.
|
||||
* Wrap a DSM in a GarlicMessage, add the fake session to the SKM.
|
||||
*
|
||||
* @param skm encrypt dsm with this skm non-null
|
||||
* @return null on error
|
||||
* @since 0.9.12
|
||||
*/
|
||||
private static GarlicMessage wrapDSM(RouterContext ctx, SessionKeyManager skm, DeliveryStatusMessage dsm) {
|
||||
// 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,
|
||||
// remembering that key+tag so that we can decrypt it later. this means we can do the
|
||||
// garlic encryption without any ElGamal (yay)
|
||||
MessageWrapper.OneTimeSession sess = MessageWrapper.generateSession(ctx, skm);
|
||||
if (sess == null)
|
||||
return null;
|
||||
GarlicMessage msg = MessageWrapper.wrap(ctx, dsm, sess);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a clove that sends the payload to the destination
|
||||
*/
|
||||
static PayloadGarlicConfig buildDataClove(RouterContext ctx, Payload data, Destination dest, long expiration) {
|
||||
PayloadGarlicConfig clove = new PayloadGarlicConfig();
|
||||
private static PayloadGarlicConfig buildDataClove(RouterContext ctx, Payload data, Destination dest, long expiration) {
|
||||
|
||||
DeliveryInstructions instructions = new DeliveryInstructions();
|
||||
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_DESTINATION);
|
||||
@@ -203,6 +280,7 @@ class OutboundClientMessageJobHelper {
|
||||
//instructions.setDelaySeconds(0);
|
||||
//instructions.setEncrypted(false);
|
||||
|
||||
PayloadGarlicConfig clove = new PayloadGarlicConfig();
|
||||
clove.setCertificate(Certificate.NULL_CERT);
|
||||
clove.setDeliveryInstructions(instructions);
|
||||
clove.setExpiration(expiration);
|
||||
@@ -221,7 +299,7 @@ class OutboundClientMessageJobHelper {
|
||||
/**
|
||||
* Build a clove that stores the leaseSet locally
|
||||
*/
|
||||
static PayloadGarlicConfig buildLeaseSetClove(RouterContext ctx, long expiration, LeaseSet replyLeaseSet) {
|
||||
private static PayloadGarlicConfig buildLeaseSetClove(RouterContext ctx, long expiration, LeaseSet replyLeaseSet) {
|
||||
PayloadGarlicConfig clove = new PayloadGarlicConfig();
|
||||
clove.setCertificate(Certificate.NULL_CERT);
|
||||
clove.setDeliveryInstructions(DeliveryInstructions.LOCAL);
|
||||
|
@@ -189,7 +189,7 @@ public class MessageWrapper {
|
||||
*
|
||||
* @since 0.9.9
|
||||
*/
|
||||
private static OneTimeSession generateSession(RouterContext ctx, SessionKeyManager skm) {
|
||||
public static OneTimeSession generateSession(RouterContext ctx, SessionKeyManager skm) {
|
||||
SessionKey key = ctx.keyGenerator().generateSessionKey();
|
||||
SessionTag tag = new SessionTag(true);
|
||||
Set<SessionTag> tags = new RemovableSingletonSet<SessionTag>(tag);
|
||||
@@ -197,6 +197,20 @@ public class MessageWrapper {
|
||||
return new OneTimeSession(key, tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Garlic wrap a message from nobody, destined for an unknown router,
|
||||
* to hide the contents from the IBGW.
|
||||
* Uses a supplied one-time session key tag for AES encryption,
|
||||
* avoiding ElGamal.
|
||||
*
|
||||
* @param session non-null
|
||||
* @return null on encrypt failure
|
||||
* @since 0.9.12
|
||||
*/
|
||||
public static GarlicMessage wrap(RouterContext ctx, I2NPMessage m, OneTimeSession session) {
|
||||
return wrap(ctx, m, session.key, session.tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Garlic wrap a message from nobody, destined for an unknown router,
|
||||
* to hide the contents from the IBGW.
|
||||
|
Reference in New Issue
Block a user