forked from I2P_Developers/i2p.i2p
OCMOSJ: Select target key in LS based on local client's support
Hook new SKMs and ending into Garlic Message encryption/decryption Remove unused wrappedKey param from buildMessage() Log tweaks and javadoc fixes WIP
This commit is contained in:
@ -14,17 +14,22 @@ import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.crypto.EncType;
|
||||
import net.i2p.crypto.SessionKeyManager;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
import net.i2p.data.i2np.GarlicClove;
|
||||
import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.router.LeaseSetKeys;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.crypto.ratchet.MuxedSKM;
|
||||
import net.i2p.router.crypto.ratchet.RatchetSKM;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -34,10 +39,14 @@ import net.i2p.util.Log;
|
||||
public class GarlicMessageBuilder {
|
||||
|
||||
/**
|
||||
* ELGAMAL_2048 only.
|
||||
*
|
||||
* @param local non-null; do not use this method for the router's SessionKeyManager
|
||||
* @param minTagOverride 0 for no override, > 0 to override SKM's settings
|
||||
*/
|
||||
static boolean needsTags(RouterContext ctx, PublicKey key, Hash local, int minTagOverride) {
|
||||
if (key.getType() == EncType.ECIES_X25519)
|
||||
return false;
|
||||
SessionKeyManager skm = ctx.clientManager().getClientSessionKeyManager(local);
|
||||
if (skm == null)
|
||||
return true;
|
||||
@ -51,6 +60,7 @@ public class GarlicMessageBuilder {
|
||||
|
||||
/**
|
||||
* Unused and probably a bad idea.
|
||||
* ELGAMAL_2048 only.
|
||||
*
|
||||
* Used below only on a recursive call if the garlic message contains a garlic message.
|
||||
* We don't need the SessionKey or SesssionTags returned
|
||||
@ -60,6 +70,7 @@ public class GarlicMessageBuilder {
|
||||
*
|
||||
* @param ctx scope
|
||||
* @param config how/what to wrap
|
||||
* @return null if expired
|
||||
* @throws IllegalArgumentException on error
|
||||
*/
|
||||
private static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config) {
|
||||
@ -70,14 +81,17 @@ public class GarlicMessageBuilder {
|
||||
|
||||
/**
|
||||
* Now unused, since we have to generate a reply token first in OCMOSJ but we don't know if tags are required yet.
|
||||
* ELGAMAL_2048 only.
|
||||
*
|
||||
* @param ctx scope
|
||||
* @param config how/what to wrap
|
||||
* @param wrappedKey output parameter that will be filled with the sessionKey used
|
||||
* @param wrappedKey non-null with null data,
|
||||
* output parameter that will be filled with the SessionKey used
|
||||
* @param wrappedTags Output parameter that will be filled with the sessionTags used.
|
||||
If non-empty on return you must call skm.tagsDelivered() when sent
|
||||
and then call skm.tagsAcked() or skm.failTags() later.
|
||||
* @param skm non-null
|
||||
* @return null if expired
|
||||
* @throws IllegalArgumentException on error
|
||||
*/
|
||||
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set<SessionTag> wrappedTags,
|
||||
@ -94,11 +108,13 @@ public class GarlicMessageBuilder {
|
||||
***/
|
||||
|
||||
/**
|
||||
* called by OCMJH
|
||||
* ELGAMAL_2048 only.
|
||||
* Called by OCMJH.
|
||||
*
|
||||
* @param ctx scope
|
||||
* @param config how/what to wrap
|
||||
* @param wrappedKey output parameter that will be filled with the sessionKey used
|
||||
* @param wrappedKey non-null with null data,
|
||||
* output parameter that will be filled with the SessionKey used
|
||||
* @param wrappedTags Output parameter that will be filled with the sessionTags used.
|
||||
If non-empty on return you must call skm.tagsDelivered() when sent
|
||||
and then call skm.tagsAcked() or skm.failTags() later.
|
||||
@ -106,6 +122,7 @@ public class GarlicMessageBuilder {
|
||||
Set to zero to disable tag delivery. You must set to zero if you are not
|
||||
equipped to confirm delivery and call skm.tagsAcked() or skm.failTags() later.
|
||||
* @param skm non-null
|
||||
* @return null if expired
|
||||
* @throws IllegalArgumentException on error
|
||||
*/
|
||||
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set<SessionTag> wrappedTags,
|
||||
@ -114,11 +131,13 @@ public class GarlicMessageBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* called by netdb and above
|
||||
* ELGAMAL_2048 only.
|
||||
* Called by netdb and above.
|
||||
*
|
||||
* @param ctx scope
|
||||
* @param config how/what to wrap
|
||||
* @param wrappedKey output parameter that will be filled with the sessionKey used
|
||||
* @param wrappedKey non-null with null data,
|
||||
* output parameter that will be filled with the SessionKey used
|
||||
* @param wrappedTags Output parameter that will be filled with the sessionTags used.
|
||||
If non-empty on return you must call skm.tagsDelivered() when sent
|
||||
and then call skm.tagsAcked() or skm.failTags() later.
|
||||
@ -128,6 +147,7 @@ public class GarlicMessageBuilder {
|
||||
If this is always 0, it forces ElGamal every time.
|
||||
* @param lowTagsThreshold the threshold
|
||||
* @param skm non-null
|
||||
* @return null if expired
|
||||
* @throws IllegalArgumentException on error
|
||||
*/
|
||||
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set<SessionTag> wrappedTags,
|
||||
@ -144,14 +164,14 @@ public class GarlicMessageBuilder {
|
||||
} else
|
||||
key = config.getRecipient().getIdentity().getPublicKey();
|
||||
}
|
||||
if (key.getType() != EncType.ELGAMAL_2048)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("Encrypted with public key to expire on " + new Date(config.getExpiration()));
|
||||
|
||||
SessionKey curKey = skm.getCurrentOrNewKey(key);
|
||||
SessionTag curTag = null;
|
||||
|
||||
curTag = skm.consumeNextAvailableTag(key, curKey);
|
||||
SessionTag curTag = skm.consumeNextAvailableTag(key, curKey);
|
||||
|
||||
if (log.shouldLog(Log.DEBUG)) {
|
||||
int availTags = skm.getAvailableTags(key, curKey);
|
||||
@ -167,25 +187,26 @@ public class GarlicMessageBuilder {
|
||||
|
||||
wrappedKey.setData(curKey.getData());
|
||||
|
||||
return buildMessage(ctx, config, wrappedKey, wrappedTags, key, curKey, curTag);
|
||||
return buildMessage(ctx, config, wrappedTags, key, curKey, curTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* used by TestJob and directly above
|
||||
* and for encrypting DatabaseLookupMessages
|
||||
* ELGAMAL_2048 only.
|
||||
* Used by TestJob, and directly above,
|
||||
* and by MessageWrapper for encrypting DatabaseLookupMessages
|
||||
*
|
||||
* @param ctx scope
|
||||
* @param config how/what to wrap
|
||||
* @param wrappedKey unused - why??
|
||||
* @param wrappedTags New tags to be sent along with the message.
|
||||
* 200 max enforced at receiver; null OK
|
||||
* @param target public key of the location being garlic routed to (may be null if we
|
||||
* know the encryptKey and encryptTag)
|
||||
* @param encryptKey sessionKey used to encrypt the current message, non-null
|
||||
* @param encryptTag sessionTag used to encrypt the current message, null to force ElG
|
||||
* @return null if expired
|
||||
* @throws IllegalArgumentException on error
|
||||
*/
|
||||
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set<SessionTag> wrappedTags,
|
||||
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, Set<SessionTag> wrappedTags,
|
||||
PublicKey target, SessionKey encryptKey, SessionTag encryptTag) {
|
||||
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
|
||||
if (config == null)
|
||||
@ -199,6 +220,11 @@ public class GarlicMessageBuilder {
|
||||
|
||||
// TODO - 128 is the minimum padded size - should it be more? less? random?
|
||||
byte encData[] = ctx.elGamalAESEngine().encrypt(cloveSet, target, encryptKey, wrappedTags, encryptTag, 128);
|
||||
if (encData == null) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("ElG encrypt fail");
|
||||
return null;
|
||||
}
|
||||
msg.setData(encData);
|
||||
msg.setMessageExpiration(config.getExpiration());
|
||||
|
||||
@ -216,6 +242,69 @@ public class GarlicMessageBuilder {
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* ECIES_X25519 only.
|
||||
* Called by GarlicMessageBuilder only.
|
||||
*
|
||||
* @param ctx scope
|
||||
* @param config how/what to wrap
|
||||
* @param target public key of the location being garlic routed to (may be null if we
|
||||
* know the encryptKey and encryptTag)
|
||||
* @return null if expired or on other errors
|
||||
* @throws IllegalArgumentException on error
|
||||
* @since 0.9.44
|
||||
*/
|
||||
static GarlicMessage buildECIESMessage(RouterContext ctx, GarlicConfig config,
|
||||
PublicKey target, Hash from, SessionKeyManager skm) {
|
||||
PublicKey key = config.getRecipientPublicKey();
|
||||
if (key.getType() != EncType.ECIES_X25519)
|
||||
throw new IllegalArgumentException();
|
||||
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
|
||||
GarlicMessage msg = new GarlicMessage(ctx);
|
||||
byte cloveSet[] = buildCloveSet(ctx, config);
|
||||
LeaseSetKeys lsk = ctx.keyManager().getKeys(from);
|
||||
if (lsk == null) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("No LSK for " + from.toBase32());
|
||||
return null;
|
||||
}
|
||||
PrivateKey priv = lsk.getDecryptionKey(EncType.ECIES_X25519);
|
||||
if (priv == null) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("No key for " + from.toBase32());
|
||||
return null;
|
||||
}
|
||||
|
||||
RatchetSKM rskm;
|
||||
if (skm instanceof RatchetSKM) {
|
||||
rskm = (RatchetSKM) skm;
|
||||
} else if (skm instanceof MuxedSKM) {
|
||||
rskm = ((MuxedSKM) skm).getECSKM();
|
||||
} else {
|
||||
if (log.shouldWarn())
|
||||
log.warn("No SKM for " + from.toBase32());
|
||||
return null;
|
||||
}
|
||||
byte encData[] = ctx.eciesEngine().encrypt(cloveSet, target, priv, rskm, config.getExpiration());
|
||||
if (encData == null) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("Encrypt fail for " + from.toBase32());
|
||||
return null;
|
||||
}
|
||||
msg.setData(encData);
|
||||
msg.setMessageExpiration(config.getExpiration());
|
||||
long timeFromNow = config.getExpiration() - ctx.clock().now();
|
||||
if (timeFromNow < 1*1000) {
|
||||
if (log.shouldDebug())
|
||||
log.debug("Building a message expiring in " + timeFromNow + "ms: " + config, new Exception("created by"));
|
||||
return null;
|
||||
}
|
||||
if (log.shouldDebug())
|
||||
log.debug("CloveSet (" + config.getCloveCount() + " cloves) for message " + msg.getUniqueId() + " is " + cloveSet.length
|
||||
+ " bytes and encrypted message data is " + encData.length + " bytes");
|
||||
return msg;
|
||||
}
|
||||
|
||||
/****
|
||||
private static void noteWrap(RouterContext ctx, GarlicMessage wrapper, GarlicConfig contained) {
|
||||
for (int i = 0; i < contained.getCloveCount(); i++) {
|
||||
@ -237,8 +326,7 @@ public class GarlicMessageBuilder {
|
||||
* @throws IllegalArgumentException on error
|
||||
*/
|
||||
private static byte[] buildCloveSet(RouterContext ctx, GarlicConfig config) {
|
||||
ByteArrayOutputStream baos = null;
|
||||
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
|
||||
ByteArrayOutputStream baos;
|
||||
try {
|
||||
if (config instanceof PayloadGarlicConfig) {
|
||||
byte clove[] = buildClove(ctx, (PayloadGarlicConfig)config);
|
||||
@ -253,7 +341,7 @@ public class GarlicMessageBuilder {
|
||||
//log.debug("Subclove IS a payload garlic clove");
|
||||
cloves[i] = buildClove(ctx, (PayloadGarlicConfig)c);
|
||||
} else {
|
||||
log.debug("Subclove IS NOT a payload garlic clove");
|
||||
//log.debug("Subclove IS NOT a payload garlic clove");
|
||||
// See notes below
|
||||
cloves[i] = buildClove(ctx, c);
|
||||
}
|
||||
@ -271,10 +359,8 @@ public class GarlicMessageBuilder {
|
||||
DataHelper.writeLong(baos, 4, config.getId());
|
||||
DataHelper.writeLong(baos, DataHelper.DATE_LENGTH, config.getExpiration());
|
||||
} catch (IOException ioe) {
|
||||
log.error("Error building the clove set", ioe);
|
||||
throw new IllegalArgumentException("Error building the clove set", ioe);
|
||||
} catch (DataFormatException dfe) {
|
||||
log.error("Error building the clove set", dfe);
|
||||
throw new IllegalArgumentException("Error building the clove set", dfe);
|
||||
}
|
||||
return baos.toByteArray();
|
||||
|
@ -10,6 +10,7 @@ package net.i2p.router.message;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import net.i2p.crypto.EncType;
|
||||
import net.i2p.crypto.SessionKeyManager;
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.DataFormatException;
|
||||
@ -18,6 +19,8 @@ import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.i2np.GarlicClove;
|
||||
import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.crypto.ratchet.MuxedSKM;
|
||||
import net.i2p.router.crypto.ratchet.RatchetSKM;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -41,16 +44,45 @@ public class GarlicMessageParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports both ELGAMAL_2048 and ECIES_X25519.
|
||||
*
|
||||
* @param encryptionKey either type TODO need both for muxed
|
||||
* @param skm use tags from this session key manager
|
||||
* @return null on error
|
||||
*/
|
||||
public CloveSet getGarlicCloves(GarlicMessage message, PrivateKey encryptionKey, SessionKeyManager skm) {
|
||||
CloveSet getGarlicCloves(GarlicMessage message, PrivateKey encryptionKey, SessionKeyManager skm) {
|
||||
byte encData[] = message.getData();
|
||||
byte decrData[];
|
||||
try {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Decrypting with private key " + encryptionKey);
|
||||
decrData = _context.elGamalAESEngine().decrypt(encData, encryptionKey, skm);
|
||||
EncType type = encryptionKey.getType();
|
||||
if (type == EncType.ELGAMAL_2048) {
|
||||
decrData = _context.elGamalAESEngine().decrypt(encData, encryptionKey, skm);
|
||||
} else if (type == EncType.ECIES_X25519) {
|
||||
RatchetSKM rskm;
|
||||
if (skm instanceof RatchetSKM) {
|
||||
rskm = (RatchetSKM) skm;
|
||||
} else if (skm instanceof MuxedSKM) {
|
||||
rskm = ((MuxedSKM) skm).getECSKM();
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("No SKM to decrypt ECIES");
|
||||
return null;
|
||||
}
|
||||
decrData = _context.eciesEngine().decrypt(encData, encryptionKey, rskm);
|
||||
if (decrData != null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("ECIES decrypt success, length: " + decrData.length);
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("ECIES decrypt fail");
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Can't decrypt with key type " + type);
|
||||
return null;
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error decrypting", dfe);
|
||||
@ -63,7 +95,10 @@ public class GarlicMessageParser {
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
return readCloveSet(decrData);
|
||||
CloveSet rv = readCloveSet(decrData, 0);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Got cloves: " + rv.getCloveCount());
|
||||
return rv;
|
||||
} catch (DataFormatException dfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to read cloveSet", dfe);
|
||||
@ -72,13 +107,15 @@ public class GarlicMessageParser {
|
||||
}
|
||||
}
|
||||
|
||||
private CloveSet readCloveSet(byte data[]) throws DataFormatException {
|
||||
int offset = 0;
|
||||
|
||||
/**
|
||||
* @param offset where in data to start
|
||||
* @return non-null, throws on all errors
|
||||
*/
|
||||
private CloveSet readCloveSet(byte data[], int offset) throws DataFormatException {
|
||||
int numCloves = data[offset] & 0xff;
|
||||
offset++;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("# cloves to read: " + numCloves);
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("# cloves to read: " + numCloves);
|
||||
if (numCloves <= 0 || numCloves > MAX_CLOVES)
|
||||
throw new DataFormatException("bad clove count " + numCloves);
|
||||
GarlicClove[] cloves = new GarlicClove[numCloves];
|
||||
|
@ -8,6 +8,7 @@ package net.i2p.router.message;
|
||||
*
|
||||
*/
|
||||
|
||||
import net.i2p.crypto.EncType;
|
||||
import net.i2p.crypto.SessionKeyManager;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
@ -21,7 +22,7 @@ import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Unencrypt a garlic message and pass off any valid cloves to the configured
|
||||
* Decrypt a garlic message and pass off any valid cloves to the configured
|
||||
* receiver to dispatch as they choose.
|
||||
*
|
||||
*/
|
||||
@ -61,7 +62,16 @@ public class GarlicMessageReceiver {
|
||||
LeaseSetKeys keys = _context.keyManager().getKeys(_clientDestination);
|
||||
skm = _context.clientManager().getClientSessionKeyManager(_clientDestination);
|
||||
if (keys != null && skm != null) {
|
||||
// TODO need to pass both keys if available for muxed decrypt
|
||||
decryptionKey = keys.getDecryptionKey();
|
||||
if (decryptionKey == null) {
|
||||
decryptionKey = keys.getDecryptionKey(EncType.ECIES_X25519);
|
||||
if (decryptionKey == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("No key to decrypt for " + _clientDestination.toBase32());
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Not trying to decrypt a garlic routed message to a disconnected client");
|
||||
@ -72,6 +82,7 @@ public class GarlicMessageReceiver {
|
||||
skm = _context.sessionKeyManager();
|
||||
}
|
||||
|
||||
// TODO need to pass both keys if available for muxed decrypt
|
||||
CloveSet set = _context.garlicMessageParser().getGarlicCloves(message, decryptionKey, skm);
|
||||
if (set != null) {
|
||||
for (int i = 0; i < set.getCloveCount(); i++) {
|
||||
@ -79,9 +90,13 @@ public class GarlicMessageReceiver {
|
||||
handleClove(clove);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("CloveMessageParser failed to decrypt the message [" + message.getUniqueId()
|
||||
+ "]", new Exception("Decrypt garlic failed"));
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
String d = (_clientDestination != null) ? _clientDestination.toBase32() : "the router";
|
||||
_log.warn("CloveMessageParser failed to decrypt the " + message.getData().length +
|
||||
" byte message [" + message.getUniqueId()
|
||||
+ "] for " + d + " with key " + decryptionKey.getType(),
|
||||
new Exception("Decrypt garlic failed"));
|
||||
}
|
||||
_context.statManager().addRateData("crypto.garlic.decryptFail", 1);
|
||||
_context.messageHistory().messageProcessingError(message.getUniqueId(),
|
||||
message.getClass().getName(),
|
||||
@ -110,10 +125,10 @@ public class GarlicMessageReceiver {
|
||||
boolean rv = invalidReason == null;
|
||||
if (!rv) {
|
||||
String howLongAgo = DataHelper.formatDuration(_context.clock().now()-clove.getExpiration().getTime());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Clove is NOT valid: id=" + clove.getCloveId()
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Clove is NOT valid: id=" + clove.getCloveId()
|
||||
+ " expiration " + howLongAgo + " ago", new Exception("Invalid within..."));
|
||||
else if (_log.shouldLog(Log.WARN))
|
||||
else if (_log.shouldWarn())
|
||||
_log.warn("Clove is NOT valid: id=" + clove.getCloveId()
|
||||
+ " expiration " + howLongAgo + " ago: " + invalidReason + ": " + clove);
|
||||
_context.messageHistory().messageProcessingError(clove.getCloveId(),
|
||||
|
@ -10,6 +10,7 @@ package net.i2p.router.message;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.crypto.EncType;
|
||||
import net.i2p.crypto.SessionKeyManager;
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.Destination;
|
||||
@ -72,9 +73,8 @@ class OutboundClientMessageJobHelper {
|
||||
*
|
||||
* For now, its just a tunneled DeliveryStatusMessage
|
||||
*
|
||||
* Unused?
|
||||
*
|
||||
* @param wrappedKey output parameter that will be filled with the sessionKey used
|
||||
* @param wrappedKey non-null with null data,
|
||||
* output parameter that will be filled with the SessionKey used
|
||||
* @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)
|
||||
@ -101,7 +101,8 @@ class OutboundClientMessageJobHelper {
|
||||
*
|
||||
* @param tagsToSendOverride if > 0, use this instead of skm's default
|
||||
* @param lowTagsOverride if > 0, use this instead of skm's default
|
||||
* @param wrappedKey output parameter that will be filled with the sessionKey used
|
||||
* @param wrappedKey non-null with null data,
|
||||
* 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
|
||||
@ -120,11 +121,16 @@ class OutboundClientMessageJobHelper {
|
||||
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();
|
||||
GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, config, wrappedKey, wrappedTags,
|
||||
tagsToSend, lowThreshold, skm);
|
||||
GarlicMessage msg;
|
||||
if (recipientPK.getType() == EncType.ECIES_X25519) {
|
||||
msg = GarlicMessageBuilder.buildECIESMessage(ctx, config, recipientPK, from, skm);
|
||||
} else {
|
||||
// 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();
|
||||
msg = GarlicMessageBuilder.buildMessage(ctx, config, wrappedKey, wrappedTags,
|
||||
tagsToSend, lowThreshold, skm);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
@ -150,7 +156,8 @@ class OutboundClientMessageJobHelper {
|
||||
ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE),
|
||||
expiration, DeliveryInstructions.LOCAL);
|
||||
|
||||
if (requireAck) {
|
||||
// for now, skip this for ratchet
|
||||
if (requireAck && recipientPK.getType() == EncType.ELGAMAL_2048) {
|
||||
// extend the expiration of the return message
|
||||
PayloadGarlicConfig ackClove = buildAckClove(ctx, from, replyTunnel, replyToken,
|
||||
expiration + ACK_EXTRA_EXPIRATION, skm);
|
||||
|
@ -32,6 +32,7 @@ import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.router.ClientMessage;
|
||||
import net.i2p.router.JobImpl;
|
||||
import net.i2p.router.LeaseSetKeys;
|
||||
import net.i2p.router.MessageSelector;
|
||||
import net.i2p.router.ReplyJob;
|
||||
import net.i2p.router.Router;
|
||||
@ -116,6 +117,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
private LeaseSet _leaseSet;
|
||||
/** Actual lease the message is being routed through */
|
||||
private Lease _lease;
|
||||
/** Actual target encryption key from the LS being used */
|
||||
private PublicKey _encryptionKey;
|
||||
private final long _start;
|
||||
/** note we can succeed after failure, but not vice versa */
|
||||
private enum Result {NONE, FAIL, SUCCESS}
|
||||
@ -373,8 +376,13 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose a lease from his leaseset to send the message to. Sets _lease.
|
||||
* Choose a lease from his leaseset to send the message to.
|
||||
*
|
||||
* Side effects:
|
||||
* Sets _lease.
|
||||
* Sets _wantACK if it's new or changed.
|
||||
* Sets _encryptionKey.
|
||||
*
|
||||
* Does several checks to see if we can actually send to this leaseset,
|
||||
* and returns nonzero failure code if unable to.
|
||||
*
|
||||
@ -399,12 +407,21 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
lsType != DatabaseEntry.KEY_TYPE_LS2) {
|
||||
return MessageStatusMessage.STATUS_SEND_FAILURE_BAD_LEASESET;
|
||||
}
|
||||
PublicKey pk = _leaseSet.getEncryptionKey();
|
||||
if (pk == null)
|
||||
|
||||
// select an encryption key from the leaseset
|
||||
Set<EncType> supported;
|
||||
LeaseSetKeys ourKeys = getContext().keyManager().getKeys(_from);
|
||||
if (ourKeys != null)
|
||||
supported = ourKeys.getSupportedEncryption();
|
||||
else
|
||||
supported = LeaseSetKeys.SET_ELG;
|
||||
_encryptionKey = _leaseSet.getEncryptionKey(supported);
|
||||
if (_encryptionKey == null) {
|
||||
if (_leaseSet.getEncryptionKey() != null)
|
||||
return MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION;
|
||||
// no keys at all?
|
||||
return MessageStatusMessage.STATUS_SEND_FAILURE_BAD_LEASESET;
|
||||
EncType encType = pk.getType();
|
||||
if (encType == null || !encType.isAvailable())
|
||||
return MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION;
|
||||
}
|
||||
|
||||
// Use the same lease if it's still good
|
||||
// Even if _leaseSet changed, _leaseSet.getEncryptionKey() didn't...
|
||||
@ -590,7 +607,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
int tagsRequired = SendMessageOptions.getTagThreshold(sendFlags);
|
||||
boolean wantACK = _wantACK ||
|
||||
shouldRequestReply ||
|
||||
GarlicMessageBuilder.needsTags(getContext(), _leaseSet.getEncryptionKey(),
|
||||
GarlicMessageBuilder.needsTags(getContext(), _encryptionKey,
|
||||
_from.calculateHash(), tagsRequired);
|
||||
|
||||
LeaseSet replyLeaseSet;
|
||||
@ -627,17 +644,14 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
dieFatal(MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION);
|
||||
return;
|
||||
}
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug(getJobId() + ": Clove built to " + _toString);
|
||||
|
||||
PublicKey key = _leaseSet.getEncryptionKey();
|
||||
SessionKey sessKey = new SessionKey();
|
||||
Set<SessionTag> tags = new HashSet<SessionTag>();
|
||||
|
||||
// Per-message flag > 0 overrides per-session option
|
||||
int tagsToSend = SendMessageOptions.getTagsToSend(sendFlags);
|
||||
GarlicMessage msg = OutboundClientMessageJobHelper.createGarlicMessage(getContext(), token,
|
||||
_overallExpiration, key,
|
||||
_overallExpiration, _encryptionKey,
|
||||
clove, _from.calculateHash(),
|
||||
_to, _inTunnel, tagsToSend,
|
||||
tagsRequired, sessKey, tags,
|
||||
@ -664,7 +678,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
if (!tags.isEmpty()) {
|
||||
SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_from.calculateHash());
|
||||
if (skm != null)
|
||||
tsh = skm.tagsDelivered(_leaseSet.getEncryptionKey(), sessKey, tags);
|
||||
tsh = skm.tagsDelivered(_encryptionKey, sessKey, tags);
|
||||
}
|
||||
onReply = new SendSuccessJob(getContext(), sessKey, tsh);
|
||||
onFail = new SendTimeoutJob(getContext(), sessKey, tsh);
|
||||
@ -1019,7 +1033,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
if (_key != null && _tags != null && _leaseSet != null) {
|
||||
SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_from.calculateHash());
|
||||
if (skm != null)
|
||||
skm.tagsAcked(_leaseSet.getEncryptionKey(), _key, _tags);
|
||||
skm.tagsAcked(_encryptionKey, _key, _tags);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1110,7 +1124,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
if (_key != null && _tags != null && _leaseSet != null) {
|
||||
SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_from.calculateHash());
|
||||
if (skm != null)
|
||||
skm.failTags(_leaseSet.getEncryptionKey(), _key, _tags);
|
||||
skm.failTags(_encryptionKey, _key, _tags);
|
||||
}
|
||||
}
|
||||
if (old == Result.NONE)
|
||||
|
@ -139,7 +139,7 @@ public class MessageWrapper {
|
||||
payload.setRecipient(to);
|
||||
|
||||
SessionKey sentKey = ctx.keyGenerator().generateSessionKey();
|
||||
GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, payload, null, null,
|
||||
GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, payload, null,
|
||||
key, sentKey, null);
|
||||
return msg;
|
||||
}
|
||||
@ -234,7 +234,7 @@ public class MessageWrapper {
|
||||
m.getMessageExpiration(),
|
||||
DeliveryInstructions.LOCAL, m);
|
||||
|
||||
GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, payload, null, null,
|
||||
GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, payload, null,
|
||||
null, encryptKey, encryptTag);
|
||||
return msg;
|
||||
}
|
||||
|
Reference in New Issue
Block a user