I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects
Commit 471b5369 authored by zzz's avatar zzz
Browse files

Ratchet: Validate NS datetime block; add NS key bloom filter

parent 7404bdc4
No related branches found
No related tags found
No related merge requests found
...@@ -61,6 +61,8 @@ public final class ECIESAEADEngine { ...@@ -61,6 +61,8 @@ public final class ECIESAEADEngine {
private static final int MIN_ENCRYPTED_SIZE = MIN_ES_SIZE; private static final int MIN_ENCRYPTED_SIZE = MIN_ES_SIZE;
private static final byte[] NULLPK = new byte[KEYLEN]; private static final byte[] NULLPK = new byte[KEYLEN];
private static final int MAXPAD = 16; 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 // debug, send ACKREQ in every ES
private static final boolean ACKREQ_IN_ES = false; private static final boolean ACKREQ_IN_ES = false;
...@@ -145,6 +147,8 @@ public final class ECIESAEADEngine { ...@@ -145,6 +147,8 @@ public final class ECIESAEADEngine {
try { try {
return x_decrypt(data, targetPrivateKey, keyManager); return x_decrypt(data, targetPrivateKey, keyManager);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
if (_log.shouldWarn())
_log.warn("ECIES decrypt error", dfe);
throw dfe; throw dfe;
} catch (Exception e) { } catch (Exception e) {
_log.error("ECIES decrypt error", e); _log.error("ECIES decrypt error", e);
...@@ -176,11 +180,11 @@ public final class ECIESAEADEngine { ...@@ -176,11 +180,11 @@ public final class ECIESAEADEngine {
HandshakeState state = key.getHandshakeState(); HandshakeState state = key.getHandshakeState();
if (state == null) { if (state == null) {
if (shouldDebug) 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); decrypted = decryptExistingSession(tag, data, key, targetPrivateKey, keyManager);
} else if (data.length >= MIN_NSR_SIZE) { } else if (data.length >= MIN_NSR_SIZE) {
if (shouldDebug) 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); decrypted = decryptNewSessionReply(tag, data, state, keyManager);
} else { } else {
decrypted = null; decrypted = null;
...@@ -270,6 +274,15 @@ public final class ECIESAEADEngine { ...@@ -270,6 +274,15 @@ public final class ECIESAEADEngine {
} }
return null; 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]; byte[] bobPK = new byte[KEYLEN];
state.getRemotePublicKey().getPublicKey(bobPK, 0); state.getRemotePublicKey().getPublicKey(bobPK, 0);
...@@ -298,7 +311,13 @@ public final class ECIESAEADEngine { ...@@ -298,7 +311,13 @@ public final class ECIESAEADEngine {
} catch (DataFormatException e) { } catch (DataFormatException e) {
throw e; throw e;
} catch (Exception 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 // tell the SKM
...@@ -862,7 +881,7 @@ public final class ECIESAEADEngine { ...@@ -862,7 +881,7 @@ public final class ECIESAEADEngine {
public PLCallback() { public PLCallback() {
this(null, null); this(null, null);
} }
/** /**
* ES * ES
* @param keyManager only for ES, otherwise null * @param keyManager only for ES, otherwise null
...@@ -874,12 +893,17 @@ public final class ECIESAEADEngine { ...@@ -874,12 +893,17 @@ public final class ECIESAEADEngine {
remote = remoteKey; remote = remoteKey;
} }
public void gotDateTime(long time) { public void gotDateTime(long time) throws DataFormatException {
if (_log.shouldDebug()) if (_log.shouldDebug())
_log.debug("Got DATE block: " + DataHelper.formatTime(time)); _log.debug("Got DATE block: " + DataHelper.formatTime(time));
if (datetime != 0) if (datetime != 0)
throw new IllegalArgumentException("Multiple DATETIME blocks"); throw new DataFormatException("Multiple DATETIME blocks");
datetime = time; 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) { public void gotOptions(byte[] options, boolean isHandshake) {
......
...@@ -30,6 +30,7 @@ import net.i2p.data.PublicKey; ...@@ -30,6 +30,7 @@ import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag; import net.i2p.data.SessionTag;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
import net.i2p.router.util.DecayingHashSet;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.SimpleTimer2; import net.i2p.util.SimpleTimer2;
...@@ -49,6 +50,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener ...@@ -49,6 +50,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
protected final I2PAppContext _context; protected final I2PAppContext _context;
private volatile boolean _alive; private volatile boolean _alive;
private final HKDF _hkdf; private final HKDF _hkdf;
private final DecayingHashSet _replayFilter;
/** /**
* Let outbound session tags sit around for this long before expiring them. * Let outbound session tags sit around for this long before expiring them.
...@@ -95,17 +97,25 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener ...@@ -95,17 +97,25 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
_pendingOutboundSessions = new HashMap<PublicKey, List<OutboundSession>>(64); _pendingOutboundSessions = new HashMap<PublicKey, List<OutboundSession>>(64);
_inboundTagSets = new ConcurrentHashMap<RatchetSessionTag, RatchetTagSet>(128); _inboundTagSets = new ConcurrentHashMap<RatchetSessionTag, RatchetTagSet>(128);
_hkdf = new HKDF(context); _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 // start the precalc of Elg2 keys if it wasn't already started
context.eciesEngine().startup(); context.eciesEngine().startup();
_alive = true; _alive = true;
new CleanupEvent(); new CleanupEvent();
} }
/**
* Cannot be restarted
*/
@Override @Override
public void shutdown() { public void shutdown() {
_alive = false; _alive = false;
_inboundTagSets.clear(); _inboundTagSets.clear();
_outboundSessions.clear(); _outboundSessions.clear();
synchronized (_pendingOutboundSessions) {
_pendingOutboundSessions.clear();
}
_replayFilter.stopDecaying();
} }
private class CleanupEvent extends SimpleTimer2.TimedEvent { private class CleanupEvent extends SimpleTimer2.TimedEvent {
...@@ -159,6 +169,14 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener ...@@ -159,6 +169,14 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
throw new UnsupportedOperationException(); 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. * Inbound or outbound. Checks state.getRole() to determine.
* For outbound (NS sent), adds to list of pending inbound sessions and returns true. * 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 ...@@ -591,6 +609,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
OutboundSession old = _outboundSessions.putIfAbsent(sess.getTarget(), sess); OutboundSession old = _outboundSessions.putIfAbsent(sess.getTarget(), sess);
boolean rv = old == null; boolean rv = old == null;
if (!rv) { if (!rv) {
// TODO fix
if (isInbound && old.getLastUsedDate() < _context.clock().now() - SESSION_TAG_DURATION_MS - (60*1000)) { if (isInbound && old.getLastUsedDate() < _context.clock().now() - SESSION_TAG_DURATION_MS - (60*1000)) {
_outboundSessions.put(sess.getTarget(), sess); _outboundSessions.put(sess.getTarget(), sess);
rv = true; rv = true;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment