diff --git a/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java b/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java index 3e4347d267b2e16cb8438c32acd995633cf4ba3a..fb9af25bf328e094e5915b425e23ba794aa079d6 100644 --- a/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java +++ b/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java @@ -850,16 +850,15 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener * Before the first ack, all tagsets go here. These are never expired, we rely * on the callers to call failTags() or ackTags() to remove them from this list. * Actually we now do a failsafe expire. - * Synch on _tagSets to access this. + * Unsynchronized, sync to use. * No particular order. */ private final Set<RatchetTagSet> _unackedTagSets; /** - * As tagsets are acked, they go here. - * After the first ack, new tagsets go here (i.e. presumed acked) - * In order, earliest first. + * There is only one active outbound tagset. + * Synch on _unackedTagSets to access this. */ - private final List<RatchetTagSet> _tagSets; + private RatchetTagSet _tagSet; private final ConcurrentHashMap<Integer, ReplyCallback> _callbacks; private final LinkedBlockingQueue<Integer> _acksToSend; /** @@ -869,11 +868,6 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener * to deliver the next set of tags. */ private volatile boolean _acked; - /** - * Fail count - * Synch on _tagSets to access this. - */ - private int _consecutiveFailures; // next key private int _myOBKeyID = -1; @@ -908,7 +902,6 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener _established = _context.clock().now(); _lastUsed = _established; _unackedTagSets = new HashSet<RatchetTagSet>(4); - _tagSets = new ArrayList<RatchetTagSet>(6); _callbacks = new ConcurrentHashMap<Integer, ReplyCallback>(); _acksToSend = new LinkedBlockingQueue<Integer>(); // generate expected tagset @@ -924,7 +917,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener RatchetTagSet tagset = new RatchetTagSet(_hkdf, state, rk, tk, _established); - _tagSets.add(tagset); + _tagSet = tagset; _state = null; if (_log.shouldDebug()) _log.debug("New OB Session, rk = " + rk + " tk = " + tk + " 1st tagset:\n" + tagset); @@ -972,7 +965,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener _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); } - synchronized (_tagSets) { + synchronized (_unackedTagSets) { _unackedTagSets.add(tagset_ba); _NSRcallback = callback; } @@ -989,16 +982,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener _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); } - synchronized (_tagSets) { - for (Iterator<RatchetTagSet> iter = _tagSets.iterator(); iter.hasNext(); ) { - RatchetTagSet set = iter.next(); - if (set.getID() == RatchetTagSet.DEBUG_OB_NSR) { - iter.remove(); - if (_log.shouldDebug()) - _log.debug("Removed OB NSR tagset:\n" + set); - } - } - _tagSets.add(tagset_ab); + synchronized (_unackedTagSets) { + _tagSet = tagset_ab; _unackedTagSets.clear(); } // We can't destroy the original state, as more NSRs may come in @@ -1019,7 +1004,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener boolean isRequest = key.isRequest(); boolean hasKey = key.getData() != null; int id = key.getID(); - synchronized (_tagSets) { + synchronized (_unackedTagSets) { if (isReverse) { // this is about my outbound tag set, // and is an ack of new key sent @@ -1038,7 +1023,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener hisLastIBKeyID = -1; else hisLastIBKeyID = _hisIBKey.getID(); - _hisIBKey = key; + // save as it may be replaced below; will be stored after all error checks complete + NextSessionKey receivedKey = key; if (hisLastIBKeyID != id) { // got a new key, use it if (hisLastIBKeyID != id - 1) { @@ -1057,7 +1043,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener if (hasKey) { // got a old key id but new data? if (_hisIBKeyWithData != null && _log.shouldWarn()) - _log.warn("Got nextkey for OB with data, didn't match previous " + key); + _log.warn("Got nextkey for OB with data: " + key + " didn't match previous " + _hisIBKey + " / " + _hisIBKeyWithData); + return; } else { if (_hisIBKeyWithData == null || _hisIBKeyWithData.getID() != key.getID()) { @@ -1076,11 +1063,9 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener else oldtsID = 1 + _myOBKeyID + hisLastIBKeyID; RatchetTagSet oldts = null; - for (RatchetTagSet ts : _tagSets) { - if (ts.getID() == oldtsID) { - oldts = ts; - break; - } + if (_tagSet != null) { + if (_tagSet.getID() == oldtsID) + oldts = _tagSet; } if (oldts == null) { if (_log.shouldWarn()) @@ -1101,6 +1086,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener _myOBKeys = nextKeys; _myOBKeyID++; } + _hisIBKey = receivedKey; + // create new OB TS, delete old one PublicKey pub = nextKeys.getPublic(); PrivateKey priv = nextKeys.getPrivate(); @@ -1111,8 +1098,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener int newtsID = oldtsID + 1; RatchetTagSet ts = new RatchetTagSet(_hkdf, oldts.getNextRootKey(), ssk, _context.clock().now(), newtsID, _myOBKeyID); - _tagSets.add(ts); - _tagSets.remove(oldts); + _tagSet = ts; _currentOBTagSetID = newtsID; if (_log.shouldWarn()) _log.warn("Got nextkey " + key + " ratchet to new OB ES TS:\n" + ts); @@ -1128,7 +1114,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener hisLastOBKeyID = -1; else hisLastOBKeyID = _hisOBKey.getID(); - _hisOBKey = key; + // save as it may be replaced below; will be stored after all error checks complete + NextSessionKey receivedKey = key; if (hisLastOBKeyID != id) { // got a new key, use it if (hisLastOBKeyID != id - 1) { @@ -1147,7 +1134,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener if (hasKey) { // got a old key id but new data? if (_hisOBKeyWithData != null && _log.shouldWarn()) - _log.warn("Got nextkey for IB with data, didn't match previous " + key); + _log.warn("Got nextkey for IB with data: " + key + " didn't match previous " + _hisOBKey + " / " + _hisOBKeyWithData); + return; } else { if (_hisOBKeyWithData == null || _hisOBKeyWithData.getID() != key.getID()) { @@ -1165,6 +1153,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener _log.warn("Got nextkey for IB but we don't have next root key " + key); return; } + int oldtsID; if (_myIBKeyID == -1 && hisLastOBKeyID == -1) oldtsID = 0; @@ -1190,6 +1179,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener _log.warn("Got reverse with request, using old key anyway " + key); _myIBKey = new NextSessionKey(_myIBKeyID, true, false); } + _hisOBKey = receivedKey; + PrivateKey sharedSecret = ECIESAEADEngine.doDH(_myIBKeys.getPrivate(), key); int newtsID = oldtsID + 1; _currentIBTagSetID = newtsID; @@ -1213,12 +1204,14 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener * @since 0.9.46 */ private NextSessionKey getReverseSendKey() { - if (_myIBKey == null) - return null; - if (_myIBKeySendCount > MAX_SEND_REVERSE_KEY) - return null; - _myIBKeySendCount++; - return _myIBKey; + synchronized (_unackedTagSets) { + if (_myIBKey == null) + return null; + if (_myIBKeySendCount > MAX_SEND_REVERSE_KEY) + return null; + _myIBKeySendCount++; + return _myIBKey; + } } /** @@ -1230,7 +1223,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener */ void firstTagConsumed(RatchetTagSet set) { SessionKey sk = set.getAssociatedKey(); - synchronized (_tagSets) { + synchronized (_unackedTagSets) { // save next root key _nextIBRootKey = set.getNextRootKey(); for (RatchetTagSet obSet : _unackedTagSets) { @@ -1239,8 +1232,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener _log.debug("First tag received from IB ES\n" + set + "\npromoting OB ES " + obSet); _unackedTagSets.clear(); - _tagSets.clear(); - _tagSets.add(obSet); + _tagSet = obSet; if (_NSRcallback != null) { _NSRcallback.onReply(); _NSRcallback = null; @@ -1252,7 +1244,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener if (_log.shouldDebug()) _log.debug("First tag received from IB ES\n" + set + " but no corresponding OB ES set found, unacked size: " + _unackedTagSets.size() + - " acked size: " + _tagSets.size()); + " acked size: " + ((_tagSet != null) ? 1 : 0)); } } @@ -1263,21 +1255,14 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener */ List<RatchetTagSet> getTagSets() { List<RatchetTagSet> rv; - synchronized (_tagSets) { + synchronized (_unackedTagSets) { rv = new ArrayList<RatchetTagSet>(_unackedTagSets); - rv.addAll(_tagSets); + if (_tagSet != null) + rv.add(_tagSet); } return rv; } - /** didn't get an ack for these tags */ - void failTags(RatchetTagSet set) { - synchronized (_tagSets) { - _unackedTagSets.remove(set); - _tagSets.remove(set); - } - } - public PublicKey getTarget() { return _target; } @@ -1309,52 +1294,43 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener */ public int expireTags(long now) { int removed = 0; - synchronized (_tagSets) { - for (Iterator<RatchetTagSet> iter = _tagSets.iterator(); iter.hasNext(); ) { + synchronized (_unackedTagSets) { + if (_tagSet != null) { + if (_tagSet.getExpiration() <= now) { + _tagSet = null; + removed++; + } + } + for (Iterator<RatchetTagSet> iter = _unackedTagSets.iterator(); iter.hasNext(); ) { RatchetTagSet set = iter.next(); if (set.getExpiration() <= now) { iter.remove(); removed++; } } - // failsafe, sometimes these are sticking around, not sure why, so clean them periodically - if ((now & 0x0f) == 0) { - for (Iterator<RatchetTagSet> iter = _unackedTagSets.iterator(); iter.hasNext(); ) { - RatchetTagSet set = iter.next(); - if (set.getExpiration() <= now) { - iter.remove(); - removed++; - } - } - } } return removed; } public RatchetEntry consumeNext() { long now = _context.clock().now(); - synchronized (_tagSets) { - while (!_tagSets.isEmpty()) { - RatchetTagSet set = _tagSets.get(0); - synchronized(set) { - if (set.getExpiration() > now) { - RatchetSessionTag tag = set.consumeNext(); - if (tag != null) { - _lastUsed = now; - set.setDate(now); - SessionKeyAndNonce skn = set.consumeNextKey(); - // TODO PN - return new RatchetEntry(tag, skn, set.getID(), 0, set.getNextKey(), - getReverseSendKey(), getAcksToSend()); - } else if (_log.shouldInfo()) { - _log.info("Removing empty " + set); - } - } else { - if (_log.shouldInfo()) - _log.info("Expired " + set); + synchronized (_unackedTagSets) { + if (_tagSet != null) { + synchronized(_tagSet) { + // use even if expired, this will reset the expiration + RatchetSessionTag tag = _tagSet.consumeNext(); + if (tag != null) { + _lastUsed = now; + _tagSet.setDate(now); + SessionKeyAndNonce skn = _tagSet.consumeNextKey(); + // TODO PN + return new RatchetEntry(tag, skn, _tagSet.getID(), 0, _tagSet.getNextKey(), + getReverseSendKey(), getAcksToSend()); + } else if (_log.shouldInfo()) { + _log.info("Removing empty " + _tagSet); } } - _tagSets.remove(0); + _tagSet = null; } } return null; @@ -1362,21 +1338,16 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener /** @return the total number of tags in acked RatchetTagSets */ public int availableTags() { - int tags = 0; long now = _context.clock().now(); - synchronized (_tagSets) { - for (int i = 0; i < _tagSets.size(); i++) { - RatchetTagSet set = _tagSets.get(i); - if (!set.getAcked()) - continue; - if (set.getExpiration() > now) { - // or just add fixed number? - int sz = set.remaining(); - tags += sz; + synchronized (_unackedTagSets) { + if (_tagSet != null) { + synchronized(_tagSet) { + if (_tagSet.getExpiration() > now) + return _tagSet.remaining(); } } } - return tags; + return 0; } /** @@ -1385,29 +1356,13 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener * */ public long getLastExpirationDate() { - long last = 0; - synchronized (_tagSets) { - for (RatchetTagSet set : _tagSets) { - long exp = set.getExpiration(); - if (exp > last && set.remaining() > 0) - last = exp; - } + synchronized (_unackedTagSets) { + if (_tagSet != null) + return _tagSet.getExpiration(); } - if (last > 0) - return last; return -1; } - /** - * Put the RatchetTagSet on the unacked list. - */ - public void addTags(RatchetTagSet set) { - _lastUsed = _context.clock().now(); - synchronized (_tagSets) { - _unackedTagSets.add(set); - } - } - public boolean getAckReceived() { return _acked; }