forked from I2P_Developers/i2p.i2p
Ratchet: Adaptive order of muxed decrypt based on previous traffic
This commit is contained in:
@ -35,12 +35,15 @@ final class MuxedEngine {
|
||||
throw new IllegalArgumentException();
|
||||
CloveSet rv = null;
|
||||
// Try in-order from fastest to slowest
|
||||
// Ratchet Tag
|
||||
rv = _context.eciesEngine().decryptFast(data, ecKey, keyManager.getECSKM());
|
||||
if (rv != null)
|
||||
return rv;
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Ratchet tag not found");
|
||||
boolean preferRatchet = keyManager.preferRatchet();
|
||||
if (preferRatchet) {
|
||||
// Ratchet Tag
|
||||
rv = _context.eciesEngine().decryptFast(data, ecKey, keyManager.getECSKM());
|
||||
if (rv != null)
|
||||
return rv;
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Ratchet tag not found before AES");
|
||||
}
|
||||
// AES Tag
|
||||
if (data.length >= 128 && (data.length & 0x0f) == 0) {
|
||||
byte[] dec = _context.elGamalAESEngine().decryptFast(data, elgKey, keyManager.getElgSKM());
|
||||
@ -48,39 +51,65 @@ final class MuxedEngine {
|
||||
try {
|
||||
rv = _context.garlicMessageParser().readCloveSet(dec, 0);
|
||||
if (rv == null && _log.shouldInfo())
|
||||
_log.info("AES cloveset error");
|
||||
_log.info("AES cloveset error after AES? " + preferRatchet);
|
||||
} catch (DataFormatException dfe) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("AES cloveset error", dfe);
|
||||
_log.info("AES cloveset error after AES? " + preferRatchet, dfe);
|
||||
}
|
||||
return rv;
|
||||
} else {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("AES tag not found");
|
||||
_log.debug("AES tag not found after ratchet? " + preferRatchet);
|
||||
}
|
||||
}
|
||||
// Ratchet DH
|
||||
rv = _context.eciesEngine().decryptSlow(data, ecKey, keyManager.getECSKM());
|
||||
if (rv != null)
|
||||
return rv;
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Ratchet NS decrypt failed");
|
||||
if (!preferRatchet) {
|
||||
// Ratchet Tag
|
||||
rv = _context.eciesEngine().decryptFast(data, ecKey, keyManager.getECSKM());
|
||||
if (rv != null)
|
||||
return rv;
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Ratchet tag not found after AES");
|
||||
}
|
||||
|
||||
if (preferRatchet) {
|
||||
// Ratchet DH
|
||||
rv = _context.eciesEngine().decryptSlow(data, ecKey, keyManager.getECSKM());
|
||||
boolean ok = rv != null;
|
||||
keyManager.reportDecryptResult(true, ok);
|
||||
if (ok)
|
||||
return rv;
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Ratchet NS decrypt failed before ElG");
|
||||
}
|
||||
// ElG DH
|
||||
if (data.length >= 514 && (data.length & 0x0f) == 2) {
|
||||
byte[] dec = _context.elGamalAESEngine().decryptSlow(data, elgKey, keyManager.getElgSKM());
|
||||
if (dec != null) {
|
||||
try {
|
||||
rv = _context.garlicMessageParser().readCloveSet(dec, 0);
|
||||
if (rv == null && _log.shouldInfo())
|
||||
_log.info("ElG cloveset error");
|
||||
boolean ok = rv != null;
|
||||
keyManager.reportDecryptResult(false, ok);
|
||||
if (ok)
|
||||
return rv;
|
||||
if (_log.shouldInfo())
|
||||
_log.info("ElG cloveset error after ratchet? " + preferRatchet);
|
||||
} catch (DataFormatException dfe) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("ElG cloveset error", dfe);
|
||||
_log.info("ElG cloveset error afterRatchet? " + preferRatchet, dfe);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("ElG decrypt failed");
|
||||
_log.info("ElG decrypt failed after Ratchet? " + preferRatchet);
|
||||
}
|
||||
keyManager.reportDecryptResult(false, false);
|
||||
}
|
||||
if (!preferRatchet) {
|
||||
// Ratchet DH
|
||||
rv = _context.eciesEngine().decryptSlow(data, ecKey, keyManager.getECSKM());
|
||||
boolean ok = rv != null;
|
||||
keyManager.reportDecryptResult(true, ok);
|
||||
if (!ok && _log.shouldDebug())
|
||||
_log.debug("Ratchet NS decrypt failed after ElG");
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package net.i2p.router.crypto.ratchet;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.EncType;
|
||||
@ -22,6 +23,11 @@ public class MuxedSKM extends SessionKeyManager {
|
||||
|
||||
private final TransientSessionKeyManager _elg;
|
||||
private final RatchetSKM _ec;
|
||||
private final AtomicInteger _elgCounter = new AtomicInteger();
|
||||
private final AtomicInteger _ecCounter = new AtomicInteger();
|
||||
// ElG is about this much slower than EC
|
||||
private static final int ELG_SLOW_FACTOR = 5;
|
||||
private static final int RESTART_COUNTERS = 500;
|
||||
|
||||
public MuxedSKM(TransientSessionKeyManager elg, RatchetSKM ec) {
|
||||
_elg = elg;
|
||||
@ -32,6 +38,42 @@ public class MuxedSKM extends SessionKeyManager {
|
||||
|
||||
public RatchetSKM getECSKM() { return _ec; }
|
||||
|
||||
/**
|
||||
* Should we try the Ratchet slow decrypt before ElG slow decrypt?
|
||||
* Adaptive test based on previous mix of traffic for this SKM,
|
||||
* as reported by reportDecryptResult().
|
||||
*
|
||||
* @since 0.9.46
|
||||
*/
|
||||
boolean preferRatchet() {
|
||||
int ec = _ecCounter.get();
|
||||
int elg = _elgCounter.get();
|
||||
if (ec > RESTART_COUNTERS / 10 &&
|
||||
elg > RESTART_COUNTERS / 10 &&
|
||||
ec + elg > RESTART_COUNTERS) {
|
||||
_ecCounter.set(0);
|
||||
_elgCounter.set(0);
|
||||
return true;
|
||||
}
|
||||
return ec >= elg / ELG_SLOW_FACTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the result of a slow decrypt attempt.
|
||||
*
|
||||
* @param isRatchet true for EC, false for ElG
|
||||
* @param success true for successful decrypt
|
||||
* @since 0.9.46
|
||||
*/
|
||||
void reportDecryptResult(boolean isRatchet, boolean success) {
|
||||
if (success) {
|
||||
if (isRatchet)
|
||||
_ecCounter.incrementAndGet();
|
||||
else
|
||||
_elgCounter.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ElG only
|
||||
*/
|
||||
|
Reference in New Issue
Block a user