Ratchet: Adaptive order of muxed decrypt based on previous traffic

This commit is contained in:
zzz
2020-04-19 14:21:24 +00:00
parent cd035e1247
commit 57cd4c5843
2 changed files with 90 additions and 19 deletions

View File

@ -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;
}

View File

@ -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
*/