forked from I2P_Developers/i2p.i2p
Ratchet: Validate NS datetime block; add NS key bloom filter
This commit is contained in:
@ -61,6 +61,8 @@ public final class ECIESAEADEngine {
|
||||
private static final int MIN_ENCRYPTED_SIZE = MIN_ES_SIZE;
|
||||
private static final byte[] NULLPK = new byte[KEYLEN];
|
||||
private static final int MAXPAD = 16;
|
||||
static final long MAX_NS_AGE = 5*60*1000;
|
||||
private static final long MAX_NS_FUTURE = 2*60*1000;
|
||||
// debug, send ACKREQ in every ES
|
||||
private static final boolean ACKREQ_IN_ES = false;
|
||||
|
||||
@ -145,6 +147,8 @@ public final class ECIESAEADEngine {
|
||||
try {
|
||||
return x_decrypt(data, targetPrivateKey, keyManager);
|
||||
} catch (DataFormatException dfe) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("ECIES decrypt error", dfe);
|
||||
throw dfe;
|
||||
} catch (Exception e) {
|
||||
_log.error("ECIES decrypt error", e);
|
||||
@ -176,11 +180,11 @@ public final class ECIESAEADEngine {
|
||||
HandshakeState state = key.getHandshakeState();
|
||||
if (state == null) {
|
||||
if (shouldDebug)
|
||||
_log.debug("Decrypting ES with tag: " + st.toBase64() + ": key: " + key.toBase64() + ": " + data.length + " bytes");
|
||||
_log.debug("Decrypting ES with tag: " + st.toBase64() + " key: " + key.toBase64() + ": " + data.length + " bytes");
|
||||
decrypted = decryptExistingSession(tag, data, key, targetPrivateKey, keyManager);
|
||||
} else if (data.length >= MIN_NSR_SIZE) {
|
||||
if (shouldDebug)
|
||||
_log.debug("Decrypting NSR with tag: " + st.toBase64() + ": key: " + key.toBase64() + ": " + data.length + " bytes");
|
||||
_log.debug("Decrypting NSR with tag: " + st.toBase64() + " key: " + key.toBase64() + ": " + data.length + " bytes");
|
||||
decrypted = decryptNewSessionReply(tag, data, state, keyManager);
|
||||
} else {
|
||||
decrypted = null;
|
||||
@ -270,6 +274,15 @@ public final class ECIESAEADEngine {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// bloom filter here based on ephemeral key
|
||||
// or should we do it based on apparent elg2-encoded key
|
||||
// at the very top, to prevent excess DH resource usage?
|
||||
// But that would put everything in the bloom filter.
|
||||
if (keyManager.isDuplicate(pk)) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Dup eph. key in IB NS: " + pk);
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] bobPK = new byte[KEYLEN];
|
||||
state.getRemotePublicKey().getPublicKey(bobPK, 0);
|
||||
@ -298,7 +311,13 @@ public final class ECIESAEADEngine {
|
||||
} catch (DataFormatException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new DataFormatException("Msg 1 payload error", e);
|
||||
throw new DataFormatException("NS payload error", e);
|
||||
}
|
||||
|
||||
if (pc.datetime == 0) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("No datetime block in IB NS");
|
||||
return null;
|
||||
}
|
||||
|
||||
// tell the SKM
|
||||
@ -862,7 +881,7 @@ public final class ECIESAEADEngine {
|
||||
public PLCallback() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ES
|
||||
* @param keyManager only for ES, otherwise null
|
||||
@ -874,12 +893,17 @@ public final class ECIESAEADEngine {
|
||||
remote = remoteKey;
|
||||
}
|
||||
|
||||
public void gotDateTime(long time) {
|
||||
public void gotDateTime(long time) throws DataFormatException {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Got DATE block: " + DataHelper.formatTime(time));
|
||||
if (datetime != 0)
|
||||
throw new IllegalArgumentException("Multiple DATETIME blocks");
|
||||
throw new DataFormatException("Multiple DATETIME blocks");
|
||||
datetime = time;
|
||||
long now = _context.clock().now();
|
||||
if (time < now - MAX_NS_AGE ||
|
||||
time > now + MAX_NS_FUTURE) {
|
||||
throw new DataFormatException("Excess clock skew in IB NS: " + DataHelper.formatTime(time));
|
||||
}
|
||||
}
|
||||
|
||||
public void gotOptions(byte[] options, boolean isHandshake) {
|
||||
|
@ -30,6 +30,7 @@ import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.util.DecayingHashSet;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
@ -49,6 +50,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
protected final I2PAppContext _context;
|
||||
private volatile boolean _alive;
|
||||
private final HKDF _hkdf;
|
||||
private final DecayingHashSet _replayFilter;
|
||||
|
||||
/**
|
||||
* Let outbound session tags sit around for this long before expiring them.
|
||||
@ -95,17 +97,25 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
_pendingOutboundSessions = new HashMap<PublicKey, List<OutboundSession>>(64);
|
||||
_inboundTagSets = new ConcurrentHashMap<RatchetSessionTag, RatchetTagSet>(128);
|
||||
_hkdf = new HKDF(context);
|
||||
_replayFilter = new DecayingHashSet(context, (int) ECIESAEADEngine.MAX_NS_AGE, 32, "Ratchet-NS");
|
||||
// start the precalc of Elg2 keys if it wasn't already started
|
||||
context.eciesEngine().startup();
|
||||
_alive = true;
|
||||
new CleanupEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cannot be restarted
|
||||
*/
|
||||
@Override
|
||||
public void shutdown() {
|
||||
_alive = false;
|
||||
_inboundTagSets.clear();
|
||||
_outboundSessions.clear();
|
||||
synchronized (_pendingOutboundSessions) {
|
||||
_pendingOutboundSessions.clear();
|
||||
}
|
||||
_replayFilter.stopDecaying();
|
||||
}
|
||||
|
||||
private class CleanupEvent extends SimpleTimer2.TimedEvent {
|
||||
@ -159,6 +169,14 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if a dup
|
||||
* @since 0.9.46
|
||||
*/
|
||||
boolean isDuplicate(PublicKey pk) {
|
||||
return _replayFilter.add(pk.getData(), 0, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inbound or outbound. Checks state.getRole() to determine.
|
||||
* For outbound (NS sent), adds to list of pending inbound sessions and returns true.
|
||||
@ -591,6 +609,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
OutboundSession old = _outboundSessions.putIfAbsent(sess.getTarget(), sess);
|
||||
boolean rv = old == null;
|
||||
if (!rv) {
|
||||
// TODO fix
|
||||
if (isInbound && old.getLastUsedDate() < _context.clock().now() - SESSION_TAG_DURATION_MS - (60*1000)) {
|
||||
_outboundSessions.put(sess.getTarget(), sess);
|
||||
rv = true;
|
||||
|
Reference in New Issue
Block a user