forked from I2P_Developers/i2p.i2p
Ratchet: Implement expiration
Store creation and last-used in tagset Catch all decrypt/encrypt exceptions Debug page improvements
This commit is contained in:
@ -125,7 +125,20 @@ public final class ECIESAEADEngine {
|
||||
*
|
||||
* @return decrypted data or null on failure
|
||||
*/
|
||||
public CloveSet decrypt(byte data[], PrivateKey targetPrivateKey, RatchetSKM keyManager) throws DataFormatException {
|
||||
public CloveSet decrypt(byte data[], PrivateKey targetPrivateKey,
|
||||
RatchetSKM keyManager) throws DataFormatException {
|
||||
try {
|
||||
return x_decrypt(data, targetPrivateKey, keyManager);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw dfe;
|
||||
} catch (Exception e) {
|
||||
_log.error("ECIES decrypt error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private CloveSet x_decrypt(byte data[], PrivateKey targetPrivateKey,
|
||||
RatchetSKM keyManager) throws DataFormatException {
|
||||
if (targetPrivateKey.getType() != EncType.ECIES_X25519)
|
||||
throw new IllegalArgumentException();
|
||||
if (data == null) {
|
||||
@ -514,6 +527,16 @@ public final class ECIESAEADEngine {
|
||||
*/
|
||||
public byte[] encrypt(CloveSet cloves, PublicKey target, PrivateKey priv,
|
||||
RatchetSKM keyManager) {
|
||||
try {
|
||||
return x_encrypt(cloves, target, priv, keyManager);
|
||||
} catch (Exception e) {
|
||||
_log.error("ECIES encrypt error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] x_encrypt(CloveSet cloves, PublicKey target, PrivateKey priv,
|
||||
RatchetSKM keyManager) {
|
||||
if (target.getType() != EncType.ECIES_X25519)
|
||||
throw new IllegalArgumentException();
|
||||
if (Arrays.equals(target.getData(), NULLPK)) {
|
||||
|
@ -31,7 +31,7 @@ import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -97,7 +97,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
// start the precalc of Elg2 keys if it wasn't already started
|
||||
context.eciesEngine().startup();
|
||||
_alive = true;
|
||||
_context.simpleTimer2().addEvent(new CleanupEvent(), 60*1000);
|
||||
new CleanupEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -107,12 +107,17 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
_outboundSessions.clear();
|
||||
}
|
||||
|
||||
private class CleanupEvent implements SimpleTimer.TimedEvent {
|
||||
private class CleanupEvent extends SimpleTimer2.TimedEvent {
|
||||
public CleanupEvent() {
|
||||
// wait until outbound expiration time to start
|
||||
super(_context.simpleTimer2(), SESSION_TAG_DURATION_MS);
|
||||
}
|
||||
|
||||
public void timeReached() {
|
||||
if (!_alive)
|
||||
return;
|
||||
// TODO
|
||||
_context.simpleTimer2().addEvent(this, 60*1000);
|
||||
aggressiveExpire();
|
||||
schedule(60*1000);
|
||||
}
|
||||
}
|
||||
|
||||
@ -520,6 +525,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
HandshakeState state = tagSet.getHandshakeState();
|
||||
synchronized(tagSet) {
|
||||
key = tagSet.consume(tag);
|
||||
if (key != null)
|
||||
tagSet.setDate(_context.clock().now());
|
||||
}
|
||||
if (key == null) {
|
||||
if (_log.shouldDebug())
|
||||
@ -563,7 +570,35 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
* @return number of tag sets expired (bogus as it overcounts inbound)
|
||||
*/
|
||||
private int aggressiveExpire() {
|
||||
return 0;
|
||||
long now = _context.clock().now();
|
||||
long exp = now - SESSION_LIFETIME_MAX_MS;
|
||||
|
||||
// inbound
|
||||
int removed = 0;
|
||||
for (Iterator<RatchetTagSet> iter = _inboundTagSets.values().iterator(); iter.hasNext();) {
|
||||
RatchetTagSet ts = iter.next();
|
||||
if (ts.getDate() < exp) {
|
||||
iter.remove();
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
if (removed > 0 && _log.shouldInfo())
|
||||
_log.info("Expired inbound: " + removed);
|
||||
|
||||
// outbound
|
||||
int oremoved = 0;
|
||||
exp = now - (SESSION_LIFETIME_MAX_MS / 2);
|
||||
for (Iterator<OutboundSession> iter = _outboundSessions.values().iterator(); iter.hasNext();) {
|
||||
OutboundSession sess = iter.next();
|
||||
oremoved += sess.expireTags();
|
||||
if (sess.getLastUsedDate() < exp) {
|
||||
iter.remove();
|
||||
oremoved++;
|
||||
}
|
||||
}
|
||||
if (oremoved > 0 && _log.shouldInfo())
|
||||
_log.info("Expired outbound: " + oremoved);
|
||||
return removed + oremoved;
|
||||
}
|
||||
|
||||
/// begin SessionTagListener ///
|
||||
@ -607,12 +642,15 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
@Override
|
||||
public void renderStatusHTML(Writer out) throws IOException {
|
||||
StringBuilder buf = new StringBuilder(1024);
|
||||
|
||||
// inbound
|
||||
buf.append("<h3 class=\"debug_inboundsessions\">Ratchet Inbound sessions</h3>" +
|
||||
"<table>");
|
||||
Map<SessionKey, Set<RatchetTagSet>> inboundSets = getRatchetTagSetsBySessionKey();
|
||||
int total = 0;
|
||||
int totalSets = 0;
|
||||
long now = _context.clock().now();
|
||||
long exp = now + SESSION_LIFETIME_MAX_MS;
|
||||
Set<RatchetTagSet> sets = new TreeSet<RatchetTagSet>(new RatchetTagSetComparator());
|
||||
for (Map.Entry<SessionKey, Set<RatchetTagSet>> e : inboundSets.entrySet()) {
|
||||
SessionKey skey = e.getKey();
|
||||
@ -623,15 +661,16 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
"<td><b>Sets:</b> ").append(sets.size()).append("</td></tr>" +
|
||||
"<tr class=\"expiry\"><td colspan=\"2\"><ul>");
|
||||
for (RatchetTagSet ts : sets) {
|
||||
int size = ts.getTags().size();
|
||||
int size = ts.size();
|
||||
total += size;
|
||||
buf.append("<li><b>ID: ").append(ts.getID());
|
||||
long expires = ts.getDate() - now;
|
||||
buf.append(" created:</b> ").append(DataHelper.formatTime(ts.getCreated()));
|
||||
long expires = exp - ts.getDate();
|
||||
if (expires > 0)
|
||||
buf.append(" expires in:</b> ").append(DataHelper.formatDuration2(expires)).append(" with ");
|
||||
buf.append(" <b>expires in:</b> ").append(DataHelper.formatDuration2(expires)).append(" with ");
|
||||
else
|
||||
buf.append(" expired:</b> ").append(DataHelper.formatDuration2(0 - expires)).append(" ago with ");
|
||||
buf.append(size).append('/').append(ts.getOriginalSize()).append(" tags remaining</li>");
|
||||
buf.append(" <b>expired:</b> ").append(DataHelper.formatDuration2(0 - expires)).append(" ago with ");
|
||||
buf.append(size).append('/').append(ts.remaining()).append(" tags remaining</li>");
|
||||
}
|
||||
buf.append("</ul></td></tr>\n");
|
||||
out.write(buf.toString());
|
||||
@ -644,11 +683,12 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
"</table>" +
|
||||
"<h3 class=\"debug_outboundsessions\">Ratchet Outbound sessions</h3>" +
|
||||
"<table>");
|
||||
total = 0;
|
||||
|
||||
// outbound
|
||||
totalSets = 0;
|
||||
exp = now + SESSION_TAG_DURATION_MS;
|
||||
Set<OutboundSession> outbound = getOutboundSessions();
|
||||
for (Iterator<OutboundSession> iter = outbound.iterator(); iter.hasNext();) {
|
||||
OutboundSession sess = iter.next();
|
||||
for (OutboundSession sess : outbound) {
|
||||
sets.clear();
|
||||
sets.addAll(sess.getTagSets());
|
||||
totalSets += sets.size();
|
||||
@ -662,20 +702,22 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
buf.append("</div></td>" +
|
||||
"<td><b># Sets:</b> ").append(sess.getTagSets().size()).append("</td></tr>" +
|
||||
"<tr><td colspan=\"2\"><ul>");
|
||||
for (Iterator<RatchetTagSet> siter = sets.iterator(); siter.hasNext();) {
|
||||
RatchetTagSet ts = siter.next();
|
||||
int size = ts.getTags().size();
|
||||
total += size;
|
||||
for (RatchetTagSet ts : sets) {
|
||||
int size = ts.remaining();
|
||||
buf.append("<li><b>ID: ").append(ts.getID())
|
||||
.append(" Sent:</b> ").append(DataHelper.formatDuration2(now - ts.getDate())).append(" ago with ");
|
||||
buf.append(size).append('/').append(ts.getOriginalSize()).append(" tags remaining; acked? ").append(ts.getAcked()).append("</li>");
|
||||
.append(" created:</b> ").append(DataHelper.formatTime(ts.getCreated()));
|
||||
long expires = exp - ts.getDate();
|
||||
if (expires > 0)
|
||||
buf.append(" <b>expires in:</b> ").append(DataHelper.formatDuration2(expires)).append(" with ");
|
||||
else
|
||||
buf.append(" <b>expired:</b> ").append(DataHelper.formatDuration2(0 - expires)).append(" ago with ");
|
||||
buf.append(size).append(" tags remaining; acked? ").append(ts.getAcked()).append("</li>");
|
||||
}
|
||||
buf.append("</ul></td></tr>\n");
|
||||
out.write(buf.toString());
|
||||
buf.setLength(0);
|
||||
}
|
||||
buf.append("<tr><th colspan=\"2\">Total outbound tags: ").append(total).append(" (")
|
||||
.append(DataHelper.formatSize2(32*total)).append("B); sets: ").append(totalSets)
|
||||
buf.append("<tr><th colspan=\"2\">Total sets: ").append(totalSets)
|
||||
.append("; sessions: ").append(outbound.size())
|
||||
.append("</th></tr>\n</table>");
|
||||
|
||||
@ -697,10 +739,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
*/
|
||||
private static class RatchetTagSetComparator implements Comparator<RatchetTagSet>, Serializable {
|
||||
public int compare(RatchetTagSet l, RatchetTagSet r) {
|
||||
int rv = (int) (l.getDate() - r.getDate());
|
||||
if (rv != 0)
|
||||
return rv;
|
||||
return l.hashCode() - r.hashCode();
|
||||
return l.getID() - r.getID();
|
||||
}
|
||||
}
|
||||
|
||||
@ -884,7 +923,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
if (_log.shouldWarn()) {
|
||||
int dropped = 0;
|
||||
for (RatchetTagSet set : _tagSets) {
|
||||
dropped += set.getTags().size();
|
||||
dropped += set.remaining();
|
||||
}
|
||||
_log.warn("Rekeyed from " + _currentKey + " to " + key
|
||||
+ ": dropping " + dropped + " session tags", new Exception());
|
||||
@ -944,6 +983,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
if (set.getDate() + SESSION_TAG_DURATION_MS > now) {
|
||||
RatchetSessionTag tag = set.consumeNext();
|
||||
if (tag != null) {
|
||||
set.setDate(now);
|
||||
SessionKeyAndNonce skn = set.consumeNextKey();
|
||||
return new RatchetEntry(tag, skn);
|
||||
} else if (_log.shouldInfo()) {
|
||||
@ -970,8 +1010,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
if (!set.getAcked())
|
||||
continue;
|
||||
if (set.getDate() + SESSION_TAG_DURATION_MS > now) {
|
||||
/////////// just add fixed number?
|
||||
int sz = set.getTags().size();
|
||||
// or just add fixed number?
|
||||
int sz = set.remaining();
|
||||
tags += sz;
|
||||
}
|
||||
}
|
||||
@ -988,7 +1028,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
long last = 0;
|
||||
synchronized (_tagSets) {
|
||||
for (RatchetTagSet set : _tagSets) {
|
||||
if ( (set.getDate() > last) && (!set.getTags().isEmpty()) )
|
||||
if (set.getDate() > last && set.remaining() > 0)
|
||||
last = set.getDate();
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package net.i2p.router.crypto.ratchet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -14,6 +13,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.HKDF;
|
||||
import net.i2p.crypto.TagSetHandle;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.SessionKey;
|
||||
|
||||
/**
|
||||
@ -38,7 +38,8 @@ class RatchetTagSet implements TagSetHandle {
|
||||
// We use byte[] for key to save space, because we don't need indexOfValueByValue()
|
||||
private final SparseArray<byte[]> _sessionKeys;
|
||||
private final HKDF hkdf;
|
||||
private final long _date;
|
||||
private final long _created;
|
||||
private long _date;
|
||||
private final int _id;
|
||||
private final int _originalSize;
|
||||
private final int _maxSize;
|
||||
@ -57,7 +58,8 @@ class RatchetTagSet implements TagSetHandle {
|
||||
private static final String INFO_4 = "SessionTagKeyGen";
|
||||
private static final String INFO_5 = "SymmetricRatchet";
|
||||
private static final byte[] ZEROLEN = new byte[0];
|
||||
private static final int TAGLEN = 8;
|
||||
private static final int TAGLEN = RatchetSessionTag.LENGTH;
|
||||
private static final int MAX = 65535;
|
||||
|
||||
/**
|
||||
* Outbound NSR Tagset
|
||||
@ -82,7 +84,7 @@ class RatchetTagSet implements TagSetHandle {
|
||||
/**
|
||||
* Inbound NSR Tagset
|
||||
*
|
||||
* @param date For inbound: when the TagSet will expire
|
||||
* @param date For inbound: creation time
|
||||
*/
|
||||
public RatchetTagSet(HKDF hkdf, SessionTagListener lsnr, HandshakeState state, SessionKey rootKey, SessionKey data,
|
||||
long date, int id, int minSize, int maxSize) {
|
||||
@ -92,7 +94,7 @@ class RatchetTagSet implements TagSetHandle {
|
||||
/**
|
||||
* Inbound ES Tagset
|
||||
*
|
||||
* @param date For inbound: when the TagSet will expire
|
||||
* @param date For inbound: creation time
|
||||
*/
|
||||
public RatchetTagSet(HKDF hkdf, SessionTagListener lsnr, SessionKey rootKey, SessionKey data,
|
||||
long date, int id, int minSize, int maxSize) {
|
||||
@ -101,13 +103,14 @@ class RatchetTagSet implements TagSetHandle {
|
||||
|
||||
|
||||
/**
|
||||
* @param date For inbound: when the TagSet will expire; for outbound: creation time
|
||||
* @param date For inbound and outbound: creation time
|
||||
*/
|
||||
private RatchetTagSet(HKDF hkdf, SessionTagListener lsnr, HandshakeState state, SessionKey rootKey, SessionKey data,
|
||||
long date, int id, boolean isInbound, int minSize, int maxSize) {
|
||||
_lsnr = lsnr;
|
||||
_state = state;
|
||||
_key = rootKey;
|
||||
_created = date;
|
||||
_date = date;
|
||||
_id = id;
|
||||
_originalSize = minSize;
|
||||
@ -161,27 +164,64 @@ class RatchetTagSet implements TagSetHandle {
|
||||
}
|
||||
|
||||
/**
|
||||
* For inbound: when the TagSet will expire; for outbound: creation time
|
||||
* For inbound and outbound: last used time
|
||||
*/
|
||||
public long getDate() {
|
||||
return _date;
|
||||
}
|
||||
|
||||
/** for debugging */
|
||||
public int getOriginalSize() {
|
||||
return 0;
|
||||
/**
|
||||
* For inbound and outbound: last used time
|
||||
*/
|
||||
public void setDate(long when) {
|
||||
_date = when;
|
||||
}
|
||||
|
||||
/**
|
||||
* For inbound and outbound: creation time
|
||||
*/
|
||||
public long getCreated() {
|
||||
return _created;
|
||||
}
|
||||
|
||||
/** for debugging */
|
||||
public int getOriginalSize() {
|
||||
return _originalSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* unused tags generated
|
||||
* @return 0 for outbound
|
||||
*/
|
||||
public int size() {
|
||||
return _sessionTags != null ? _sessionTags.size() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tags remaining
|
||||
* @return 0 - 65535
|
||||
*/
|
||||
public int remaining() {
|
||||
int nextKey;
|
||||
if (_sessionTags != null) {
|
||||
// IB
|
||||
if (_sessionTags.size() <= 0)
|
||||
nextKey = 0;
|
||||
else
|
||||
nextKey = _sessionTags.keyAt(0);
|
||||
} else {
|
||||
// OB
|
||||
nextKey = _lastTag + 1;
|
||||
}
|
||||
return MAX - nextKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* tags still available
|
||||
* inbound only
|
||||
* testing only
|
||||
*/
|
||||
public List<RatchetSessionTag> getTags() {
|
||||
private List<RatchetSessionTag> getTags() {
|
||||
if (_sessionTags == null)
|
||||
return Collections.emptyList();
|
||||
int sz = _sessionTags.size();
|
||||
@ -197,7 +237,7 @@ class RatchetTagSet implements TagSetHandle {
|
||||
* inbound only
|
||||
* testing only
|
||||
*/
|
||||
public RatchetSessionTag getFirstTag() {
|
||||
private RatchetSessionTag getFirstTag() {
|
||||
if (_sessionTags == null)
|
||||
throw new IllegalStateException("Outbound tagset");
|
||||
if (_sessionTags.size() <= 0)
|
||||
@ -343,10 +383,14 @@ class RatchetTagSet implements TagSetHandle {
|
||||
buf.append("NSR ");
|
||||
else
|
||||
buf.append("ES ");
|
||||
buf.append("TagSet #").append(_id).append(" created: ").append(new Date(_date));
|
||||
buf.append("TagSet #").append(_id)
|
||||
.append(" created: ").append(DataHelper.formatTime(_created))
|
||||
.append(" last use: ").append(DataHelper.formatTime(_date));
|
||||
int sz = size();
|
||||
buf.append(" Size: ").append(sz);
|
||||
buf.append('/').append(getOriginalSize());
|
||||
buf.append(" Size: ").append(sz)
|
||||
.append(" Orig: ").append(_originalSize)
|
||||
.append(" Max: ").append(_maxSize)
|
||||
.append(" Remaining: ").append(remaining());
|
||||
buf.append(" Acked? ").append(_acked);
|
||||
if (_sessionTags != null) {
|
||||
buf.append(" Inbound");
|
||||
|
Reference in New Issue
Block a user