Ratchet: Increase callback timeout

- Mark tagsets after ratchet, and single tagsets, as acked
  so we don't go searching for the reverse tagset
  or adjust the expiration downward
- Eliminate redundant split() calculation
This commit is contained in:
zzz
2020-04-29 21:40:25 +00:00
parent e1beeb3653
commit 4cf8bfbe0d
5 changed files with 62 additions and 31 deletions

View File

@ -509,17 +509,15 @@ public final class ECIESAEADEngine {
_log.debug("State after decrypt new session reply: " + state);
// split()
byte[] ck = state.getChainingKey();
byte[] k_ab = new byte[32];
byte[] k_ba = new byte[32];
_hkdf.calculate(ck, ZEROLEN, k_ab, k_ba, 0);
// Noise does it too but it trashes the keys
SplitKeys split = new SplitKeys(state, _hkdf);
CipherStatePair ckp = state.split();
CipherState rcvr = ckp.getReceiver();
byte[] hash = state.getHandshakeHash();
// part 2 - payload
byte[] encpayloadkey = new byte[32];
_hkdf.calculate(k_ba, ZEROLEN, INFO_6, encpayloadkey);
_hkdf.calculate(split.k_ba.getData(), ZEROLEN, INFO_6, encpayloadkey);
rcvr.initializeKey(encpayloadkey, 0);
byte[] payload = new byte[data.length - (TAGLEN + KEYLEN + MACLEN + MACLEN)];
try {
@ -561,7 +559,7 @@ public final class ECIESAEADEngine {
// tell the SKM
PublicKey bob = new PublicKey(EncType.ECIES_X25519, bobPK);
keyManager.updateSession(bob, oldState, state, null);
keyManager.updateSession(bob, oldState, state, null, split);
if (pc.cloveSet.isEmpty()) {
if (_log.shouldWarn())
@ -842,17 +840,15 @@ public final class ECIESAEADEngine {
eph.getEncodedPublicKey(enc, TAGLEN);
// split()
byte[] ck = state.getChainingKey();
byte[] k_ab = new byte[32];
byte[] k_ba = new byte[32];
_hkdf.calculate(ck, ZEROLEN, k_ab, k_ba, 0);
// Noise does it too but it trashes the keys
SplitKeys split = new SplitKeys(state, _hkdf);
CipherStatePair ckp = state.split();
CipherState sender = ckp.getSender();
byte[] hash = state.getHandshakeHash();
// part 2 - payload
byte[] encpayloadkey = new byte[32];
_hkdf.calculate(k_ba, ZEROLEN, INFO_6, encpayloadkey);
_hkdf.calculate(split.k_ba.getData(), ZEROLEN, INFO_6, encpayloadkey);
sender.initializeKey(encpayloadkey, 0);
try {
sender.encryptWithAd(hash, payload, 0, enc, TAGLEN + KEYLEN + MACLEN, payload.length);
@ -862,7 +858,7 @@ public final class ECIESAEADEngine {
return null;
}
// tell the SKM
keyManager.updateSession(target, null, state, callback);
keyManager.updateSession(target, null, state, callback, split);
return enc;
}

View File

@ -222,7 +222,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
* @param oldState null for inbound, pre-clone for outbound
* @return true if this was the first NSR received
*/
boolean updateSession(PublicKey target, HandshakeState oldState, HandshakeState state, ReplyCallback callback) {
boolean updateSession(PublicKey target, HandshakeState oldState, HandshakeState state,
ReplyCallback callback, SplitKeys split) {
EncType type = target.getType();
if (type != EncType.ECIES_X25519)
throw new IllegalArgumentException("Bad public key type " + type);
@ -238,7 +239,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
// TODO can we recover?
return false;
}
sess.updateSession(state, callback);
sess.updateSession(state, callback, split);
} else {
// we are Alice, NSR received
if (_log.shouldInfo())
@ -258,7 +259,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
if (oldState.equals(pstate)) {
if (!found) {
found = true;
sess.updateSession(state, null);
sess.updateSession(state, null, split);
boolean ok = addSession(sess, false);
if (_log.shouldDebug()) {
if (ok)
@ -966,12 +967,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
* @param state current state
* @param callback only for inbound (NSR sent by Bob), may be null
*/
void updateSession(HandshakeState state, ReplyCallback callback) {
byte[] ck = state.getChainingKey();
byte[] k_ab = new byte[32];
byte[] k_ba = new byte[32];
_hkdf.calculate(ck, ZEROLEN, k_ab, k_ba, 0);
SessionKey rk = new SessionKey(ck);
void updateSession(HandshakeState state, ReplyCallback callback, SplitKeys split) {
SessionKey rk = split.ck;
long now = _context.clock().now();
_lastUsed = now;
_lastReceived = now;
@ -979,15 +976,17 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
if (isInbound) {
// We are Bob
// This is an OUTBOUND NSR, we make an INBOUND tagset for ES
RatchetTagSet tagset_ab = new RatchetTagSet(_hkdf, RatchetSKM.this, _target, rk, new SessionKey(k_ab),
RatchetTagSet tagset_ab = new RatchetTagSet(_hkdf, RatchetSKM.this, _target, rk, split.k_ab,
now, 0, -1,
MIN_RCV_WINDOW_ES, MAX_RCV_WINDOW_ES);
// and a pending outbound one
RatchetTagSet tagset_ba = new RatchetTagSet(_hkdf, rk, new SessionKey(k_ba),
// TODO - We could just save rk and k_ba, and defer
// creation of the OB ES tagset to firstTagConsumed() below
RatchetTagSet tagset_ba = new RatchetTagSet(_hkdf, rk, split.k_ba,
now, 0, -1);
if (_log.shouldDebug()) {
_log.debug("Update IB Session, rk = " + rk + " tk = " + Base64.encode(k_ab) + " ES tagset:\n" + tagset_ab);
_log.debug("Pending OB Session, rk = " + rk + " tk = " + Base64.encode(k_ba) + " ES tagset:\n" + tagset_ba);
_log.debug("Update IB Session, rk = " + rk + " tk = " + split.k_ab + " ES tagset:\n" + tagset_ab);
_log.debug("Pending OB Session, rk = " + rk + " tk = " + split.k_ba + " ES tagset:\n" + tagset_ba);
}
synchronized (_unackedTagSets) {
_unackedTagSets.add(tagset_ba);
@ -996,15 +995,15 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
} else {
// We are Alice
// This is an INBOUND NSR, we make an OUTBOUND tagset for ES
RatchetTagSet tagset_ab = new RatchetTagSet(_hkdf, rk, new SessionKey(k_ab),
RatchetTagSet tagset_ab = new RatchetTagSet(_hkdf, rk, split.k_ab,
now, 0, -1);
// and an inbound one
RatchetTagSet tagset_ba = new RatchetTagSet(_hkdf, RatchetSKM.this, _target, rk, new SessionKey(k_ba),
RatchetTagSet tagset_ba = new RatchetTagSet(_hkdf, RatchetSKM.this, _target, rk, split.k_ba,
now, 0, -1,
MIN_RCV_WINDOW_ES, MAX_RCV_WINDOW_ES);
if (_log.shouldDebug()) {
_log.debug("Update OB Session, rk = " + rk + " tk = " + Base64.encode(k_ab) + " ES tagset:\n" + tagset_ab);
_log.debug("Update IB Session, rk = " + rk + " tk = " + Base64.encode(k_ba) + " ES tagset:\n" + tagset_ba);
_log.debug("Update OB Session, rk = " + rk + " tk = " + split.k_ab + " ES tagset:\n" + tagset_ab);
_log.debug("Update IB Session, rk = " + rk + " tk = " + split.k_ba + " ES tagset:\n" + tagset_ba);
}
synchronized (_unackedTagSets) {
_tagSet = tagset_ab;

View File

@ -173,6 +173,9 @@ class RatchetTagSet implements TagSetHandle {
_sessionTags = null;
_sessionKeys = null;
}
// prevent adjusted expiration and search for matching OB ts
if (tagsetid > 0 && tagsetid <= 65535)
_acked = true;
}
/**
@ -200,6 +203,8 @@ class RatchetTagSet implements TagSetHandle {
hkdf = null;
_sessionTags = null;
_sessionKeys = null;
// prevent adjusted expiration and search for matching OB ts
_acked = true;
}
public void clear() {

View File

@ -0,0 +1,29 @@
package net.i2p.router.crypto.ratchet;
import com.southernstorm.noise.protocol.HandshakeState;
import net.i2p.crypto.HKDF;
import net.i2p.data.SessionKey;
/**
* Standard Noise split().
* Passed from the engine to the SKM so we don't have
* to do it twice.
*
* @since 0.9.46
*/
class SplitKeys {
private static final byte[] ZEROLEN = new byte[0];
public final SessionKey ck, k_ab, k_ba;
public SplitKeys(HandshakeState state, HKDF hkdf) {
byte[] ckd = state.getChainingKey();
byte[] ab = new byte[32];
byte[] ba = new byte[32];
hkdf.calculate(ckd, ZEROLEN, ab, ba, 0);
ck = new SessionKey(ckd);
k_ab = new SessionKey(ab);
k_ba = new SessionKey(ba);
}
}

View File

@ -147,6 +147,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
private final static long LS_LOOKUP_TIMEOUT = 15*1000;
private final static long OVERALL_TIMEOUT_NOLS_MIN = OVERALL_TIMEOUT_MS_MIN + LS_LOOKUP_TIMEOUT;
private final static long REPLY_TIMEOUT_MS_MIN = OVERALL_TIMEOUT_MS_DEFAULT - 5*1000;
// callback timeout. Longer so we can have success-after-failure
private final static long RATCHET_REPLY_TIMEOUT_MS_MIN = 30*1000;
/**
* NOTE: Changed as of 0.9.2.
@ -1152,8 +1154,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
}
public long getExpiration() {
// same as SendTimeoutJob
return Math.max(_overallExpiration, _start + REPLY_TIMEOUT_MS_MIN);
// longer timeout so we can have success-after-failure via ratchet
return Math.max(_overallExpiration, _start + RATCHET_REPLY_TIMEOUT_MS_MIN);
}
public void onReply() {