* Session Keys:

- Don't instantiate unused SessionKeyPersistenceHelper
      - Use TransientSessionKeyManager instead of PersistentSessionKeyManager
      - Add generics to TransientSessionKeyManager to help understand it
      - Change initial session map size to 64 (was 1024)
      - Prepare for per-destination SessionKeyManagers in ElGamalAESEngine
This commit is contained in:
zzz
2009-05-29 10:00:06 +00:00
parent b43338bd63
commit e65c2e279b
7 changed files with 97 additions and 379 deletions

View File

@ -17,9 +17,9 @@ import net.i2p.crypto.ElGamalEngine;
import net.i2p.crypto.HMAC256Generator; import net.i2p.crypto.HMAC256Generator;
import net.i2p.crypto.HMACGenerator; import net.i2p.crypto.HMACGenerator;
import net.i2p.crypto.KeyGenerator; import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.PersistentSessionKeyManager;
import net.i2p.crypto.SHA256Generator; import net.i2p.crypto.SHA256Generator;
import net.i2p.crypto.SessionKeyManager; import net.i2p.crypto.SessionKeyManager;
import net.i2p.crypto.TransientSessionKeyManager;
import net.i2p.data.RoutingKeyGenerator; import net.i2p.data.RoutingKeyGenerator;
import net.i2p.stat.StatManager; import net.i2p.stat.StatManager;
import net.i2p.util.Clock; import net.i2p.util.Clock;
@ -256,7 +256,8 @@ public class I2PAppContext {
private void initializeSessionKeyManager() { private void initializeSessionKeyManager() {
synchronized (this) { synchronized (this) {
if (_sessionKeyManager == null) if (_sessionKeyManager == null)
_sessionKeyManager = new PersistentSessionKeyManager(this); //_sessionKeyManager = new PersistentSessionKeyManager(this);
_sessionKeyManager = new TransientSessionKeyManager(this);
_sessionKeyManagerInitialized = true; _sessionKeyManagerInitialized = true;
} }
} }

View File

@ -58,12 +58,19 @@ public class ElGamalAESEngine {
new long[] { 60 * 60 * 1000l, 24 * 60 * 60 * 1000l}); new long[] { 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
} }
/**
* Decrypt the message using the given private key using tags from the given key manager.
*/
public byte[] decrypt(byte data[], PrivateKey targetPrivateKey) throws DataFormatException {
return decrypt(data, targetPrivateKey, _context.sessionKeyManager());
}
/** /**
* Decrypt the message using the given private key. This works according to the * Decrypt the message using the given private key. This works according to the
* ElGamal+AES algorithm in the data structure spec. * ElGamal+AES algorithm in the data structure spec.
* *
*/ */
public byte[] decrypt(byte data[], PrivateKey targetPrivateKey) throws DataFormatException { public byte[] decrypt(byte data[], PrivateKey targetPrivateKey, SessionKeyManager keyManager) throws DataFormatException {
if (data == null) { if (data == null) {
if (_log.shouldLog(Log.ERROR)) _log.error("Null data being decrypted?"); if (_log.shouldLog(Log.ERROR)) _log.error("Null data being decrypted?");
return null; return null;
@ -76,7 +83,7 @@ public class ElGamalAESEngine {
byte tag[] = new byte[32]; byte tag[] = new byte[32];
System.arraycopy(data, 0, tag, 0, tag.length); System.arraycopy(data, 0, tag, 0, tag.length);
SessionTag st = new SessionTag(tag); SessionTag st = new SessionTag(tag);
SessionKey key = _context.sessionKeyManager().consumeTag(st); SessionKey key = keyManager.consumeTag(st);
SessionKey foundKey = new SessionKey(); SessionKey foundKey = new SessionKey();
foundKey.setData(null); foundKey.setData(null);
SessionKey usedKey = new SessionKey(); SessionKey usedKey = new SessionKey();
@ -124,11 +131,11 @@ public class ElGamalAESEngine {
if (foundKey.getData() != null) { if (foundKey.getData() != null) {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Found key: " + foundKey.toBase64() + " tags: " + foundTags + " wasExisting? " + wasExisting); _log.debug("Found key: " + foundKey.toBase64() + " tags: " + foundTags + " wasExisting? " + wasExisting);
_context.sessionKeyManager().tagsReceived(foundKey, foundTags); keyManager.tagsReceived(foundKey, foundTags);
} else { } else {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Used key: " + usedKey.toBase64() + " tags: " + foundTags + " wasExisting? " + wasExisting); _log.debug("Used key: " + usedKey.toBase64() + " tags: " + foundTags + " wasExisting? " + wasExisting);
_context.sessionKeyManager().tagsReceived(usedKey, foundTags); keyManager.tagsReceived(usedKey, foundTags);
} }
} }
return decrypted; return decrypted;

View File

@ -1,190 +0,0 @@
package net.i2p.crypto;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.util.Log;
/**
* Expose the functionality to allow people to write out and read in the
* session key and session tag information via streams. This implementation
* does not write anywhere except where its told.
*
*/
public class PersistentSessionKeyManager extends TransientSessionKeyManager {
private Log _log;
private Object _yk = YKGenerator.class;
/**
* The session key manager should only be constructed and accessed through the
* application context. This constructor should only be used by the
* appropriate application context itself.
*
*/
public PersistentSessionKeyManager(I2PAppContext context) {
super(context);
_log = context.logManager().getLog(PersistentSessionKeyManager.class);
}
private PersistentSessionKeyManager() {
this(null);
}
/**
* Write the session key data to the given stream
*
*/
public void saveState(OutputStream out) throws IOException, DataFormatException {
if (true) return;
Set tagSets = getInboundTagSets();
Set sessions = getOutboundSessions();
if (_log.shouldLog(Log.INFO))
_log.info("Saving state with " + tagSets.size() + " inbound tagSets and "
+ sessions.size() + " outbound sessions");
DataHelper.writeLong(out, 4, tagSets.size());
for (Iterator iter = tagSets.iterator(); iter.hasNext();) {
TagSet ts = (TagSet) iter.next();
writeTagSet(out, ts);
}
DataHelper.writeLong(out, 4, sessions.size());
for (Iterator iter = sessions.iterator(); iter.hasNext();) {
OutboundSession sess = (OutboundSession) iter.next();
writeOutboundSession(out, sess);
}
}
/**
* Load the session key data from the given stream
*
*/
public void loadState(InputStream in) throws IOException, DataFormatException {
int inboundSets = (int) DataHelper.readLong(in, 4);
Set tagSets = new HashSet(inboundSets);
for (int i = 0; i < inboundSets; i++) {
TagSet ts = readTagSet(in);
tagSets.add(ts);
}
int outboundSessions = (int) DataHelper.readLong(in, 4);
Set sessions = new HashSet(outboundSessions);
for (int i = 0; i < outboundSessions; i++) {
OutboundSession sess = readOutboundSession(in);
sessions.add(sess);
}
if (_log.shouldLog(Log.INFO))
_log.info("Loading state with " + tagSets.size() + " inbound tagSets and "
+ sessions.size() + " outbound sessions");
setData(tagSets, sessions);
}
private void writeOutboundSession(OutputStream out, OutboundSession sess) throws IOException, DataFormatException {
sess.getTarget().writeBytes(out);
sess.getCurrentKey().writeBytes(out);
DataHelper.writeDate(out, new Date(sess.getEstablishedDate()));
DataHelper.writeDate(out, new Date(sess.getLastUsedDate()));
List sets = sess.getTagSets();
DataHelper.writeLong(out, 2, sets.size());
for (Iterator iter = sets.iterator(); iter.hasNext();) {
TagSet set = (TagSet) iter.next();
writeTagSet(out, set);
}
}
private void writeTagSet(OutputStream out, TagSet ts) throws IOException, DataFormatException {
ts.getAssociatedKey().writeBytes(out);
DataHelper.writeDate(out, new Date(ts.getDate()));
DataHelper.writeLong(out, 2, ts.getTags().size());
for (Iterator iter = ts.getTags().iterator(); iter.hasNext();) {
SessionTag tag = (SessionTag) iter.next();
out.write(tag.getData());
}
}
private OutboundSession readOutboundSession(InputStream in) throws IOException, DataFormatException {
PublicKey key = new PublicKey();
key.readBytes(in);
SessionKey skey = new SessionKey();
skey.readBytes(in);
Date established = DataHelper.readDate(in);
Date lastUsed = DataHelper.readDate(in);
int tagSets = (int) DataHelper.readLong(in, 2);
ArrayList sets = new ArrayList(tagSets);
for (int i = 0; i < tagSets; i++) {
TagSet ts = readTagSet(in);
sets.add(ts);
}
return new OutboundSession(key, skey, established.getTime(), lastUsed.getTime(), sets);
}
private TagSet readTagSet(InputStream in) throws IOException, DataFormatException {
SessionKey key = new SessionKey();
key.readBytes(in);
Date date = DataHelper.readDate(in);
int numTags = (int) DataHelper.readLong(in, 2);
Set tags = new HashSet(numTags);
for (int i = 0; i < numTags; i++) {
SessionTag tag = new SessionTag();
byte val[] = new byte[SessionTag.BYTE_LENGTH];
int read = DataHelper.read(in, val);
if (read != SessionTag.BYTE_LENGTH)
throw new IOException("Unable to fully read a session tag [" + read + " not " + SessionTag.BYTE_LENGTH
+ ")");
tag.setData(val);
tags.add(tag);
}
TagSet ts = new TagSet(tags, key, _context.clock().now());
ts.setDate(date.getTime());
return ts;
}
public static void main(String args[]) {
I2PAppContext ctx = new I2PAppContext();
Log log = ctx.logManager().getLog(PersistentSessionKeyManager.class);
PersistentSessionKeyManager mgr = (PersistentSessionKeyManager)ctx.sessionKeyManager();
try {
mgr.loadState(new FileInputStream("sessionKeys.dat"));
String state = mgr.renderStatusHTML();
FileOutputStream fos = new FileOutputStream("sessionKeysBeforeExpire.html");
fos.write(state.getBytes());
fos.close();
int expired = mgr.aggressiveExpire();
log.error("Expired: " + expired);
String stateAfter = mgr.renderStatusHTML();
FileOutputStream fos2 = new FileOutputStream("sessionKeysAfterExpire.html");
fos2.write(stateAfter.getBytes());
fos2.close();
} catch (Throwable t) {
log.error("Error loading/storing sessionKeys", t);
}
try {
Thread.sleep(3000);
} catch (Throwable t) { // nop
}
}
}

View File

@ -93,7 +93,7 @@ public class SessionKeyManager {
* method after receiving an ack to a message delivering them) * method after receiving an ack to a message delivering them)
* *
*/ */
public void tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) { // nop public void tagsDelivered(PublicKey target, SessionKey key, Set<SessionTag> sessionTags) { // nop
} }
/** /**
@ -109,7 +109,7 @@ public class SessionKeyManager {
* Accept the given tags and associate them with the given key for decryption * Accept the given tags and associate them with the given key for decryption
* *
*/ */
public void tagsReceived(SessionKey key, Set sessionTags) { // nop public void tagsReceived(SessionKey key, Set<SessionTag> sessionTags) { // nop
} }
/** /**

View File

@ -33,12 +33,12 @@ import net.i2p.util.SimpleTimer;
* out to disk so this should not be considered secure in that sense. * out to disk so this should not be considered secure in that sense.
* *
*/ */
class TransientSessionKeyManager extends SessionKeyManager { public class TransientSessionKeyManager extends SessionKeyManager {
private Log _log; private Log _log;
/** Map allowing us to go from the targeted PublicKey to the OutboundSession used */ /** Map allowing us to go from the targeted PublicKey to the OutboundSession used */
private Map _outboundSessions; private Map<PublicKey, OutboundSession> _outboundSessions;
/** Map allowing us to go from a SessionTag to the containing TagSet */ /** Map allowing us to go from a SessionTag to the containing TagSet */
private Map _inboundTagSets; private Map<SessionTag, TagSet> _inboundTagSets;
protected I2PAppContext _context; protected I2PAppContext _context;
/** /**
@ -55,6 +55,10 @@ class TransientSessionKeyManager extends SessionKeyManager {
* *
*/ */
public final static long SESSION_LIFETIME_MAX_MS = SESSION_TAG_DURATION_MS + 5 * 60 * 1000; public final static long SESSION_LIFETIME_MAX_MS = SESSION_TAG_DURATION_MS + 5 * 60 * 1000;
/**
* a few MB? how about 16MB!
* This is the max size of _inboundTagSets.
*/
public final static int MAX_INBOUND_SESSION_TAGS = 500 * 1000; // this will consume at most a few MB public final static int MAX_INBOUND_SESSION_TAGS = 500 * 1000; // this will consume at most a few MB
/** /**
@ -67,7 +71,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
super(context); super(context);
_log = context.logManager().getLog(TransientSessionKeyManager.class); _log = context.logManager().getLog(TransientSessionKeyManager.class);
_context = context; _context = context;
_outboundSessions = new HashMap(1024); _outboundSessions = new HashMap(64);
_inboundTagSets = new HashMap(1024); _inboundTagSets = new HashMap(1024);
context.statManager().createRateStat("crypto.sessionTagsExpired", "How many tags/sessions are expired?", "Encryption", new long[] { 10*60*1000, 60*60*1000, 3*60*60*1000 }); context.statManager().createRateStat("crypto.sessionTagsExpired", "How many tags/sessions are expired?", "Encryption", new long[] { 10*60*1000, 60*60*1000, 3*60*60*1000 });
context.statManager().createRateStat("crypto.sessionTagsRemaining", "How many tags/sessions are remaining after a cleanup?", "Encryption", new long[] { 10*60*1000, 60*60*1000, 3*60*60*1000 }); context.statManager().createRateStat("crypto.sessionTagsRemaining", "How many tags/sessions are remaining after a cleanup?", "Encryption", new long[] { 10*60*1000, 60*60*1000, 3*60*60*1000 });
@ -85,28 +89,28 @@ class TransientSessionKeyManager extends SessionKeyManager {
} }
/** TagSet */ /** TagSet */
protected Set getInboundTagSets() { protected Set<TagSet> getInboundTagSets() {
synchronized (_inboundTagSets) { synchronized (_inboundTagSets) {
return new HashSet(_inboundTagSets.values()); return new HashSet(_inboundTagSets.values());
} }
} }
/** OutboundSession */ /** OutboundSession */
protected Set getOutboundSessions() { protected Set<OutboundSession> getOutboundSessions() {
synchronized (_outboundSessions) { synchronized (_outboundSessions) {
return new HashSet(_outboundSessions.values()); return new HashSet(_outboundSessions.values());
} }
} }
protected void setData(Set inboundTagSets, Set outboundSessions) { protected void setData(Set<TagSet> inboundTagSets, Set<OutboundSession> outboundSessions) {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info("Loading " + inboundTagSets.size() + " inbound tag sets, and " _log.info("Loading " + inboundTagSets.size() + " inbound tag sets, and "
+ outboundSessions.size() + " outbound sessions"); + outboundSessions.size() + " outbound sessions");
Map tagSets = new HashMap(inboundTagSets.size()); Map<SessionTag, TagSet> tagSets = new HashMap(inboundTagSets.size());
for (Iterator iter = inboundTagSets.iterator(); iter.hasNext();) { for (Iterator<TagSet> iter = inboundTagSets.iterator(); iter.hasNext();) {
TagSet ts = (TagSet) iter.next(); TagSet ts = iter.next();
for (Iterator tsIter = ts.getTags().iterator(); tsIter.hasNext();) { for (Iterator<SessionTag> tsIter = ts.getTags().iterator(); tsIter.hasNext();) {
SessionTag tag = (SessionTag) tsIter.next(); SessionTag tag = tsIter.next();
tagSets.put(tag, ts); tagSets.put(tag, ts);
} }
} }
@ -114,9 +118,9 @@ class TransientSessionKeyManager extends SessionKeyManager {
_inboundTagSets.clear(); _inboundTagSets.clear();
_inboundTagSets.putAll(tagSets); _inboundTagSets.putAll(tagSets);
} }
Map sessions = new HashMap(outboundSessions.size()); Map<PublicKey, OutboundSession> sessions = new HashMap(outboundSessions.size());
for (Iterator iter = outboundSessions.iterator(); iter.hasNext();) { for (Iterator<OutboundSession> iter = outboundSessions.iterator(); iter.hasNext();) {
OutboundSession sess = (OutboundSession) iter.next(); OutboundSession sess = iter.next();
sessions.put(sess.getTarget(), sess); sessions.put(sess.getTarget(), sess);
} }
synchronized (_outboundSessions) { synchronized (_outboundSessions) {
@ -151,6 +155,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
* Associate a new session key with the specified target. Metrics to determine * Associate a new session key with the specified target. Metrics to determine
* when to expire that key begin with this call. * when to expire that key begin with this call.
* *
* Unused except in tests?
*/ */
@Override @Override
public void createSession(PublicKey target, SessionKey key) { public void createSession(PublicKey target, SessionKey key) {
@ -159,6 +164,18 @@ class TransientSessionKeyManager extends SessionKeyManager {
addSession(sess); addSession(sess);
} }
/**
* Same as above but for internal use, returns OutboundSession so we don't have
* to do a subsequent getSession()
*
*/
private OutboundSession createAndReturnSession(PublicKey target, SessionKey key) {
OutboundSession sess = new OutboundSession(target);
sess.setCurrentKey(key);
addSession(sess);
return sess;
}
/** /**
* Retrieve the next available session tag for identifying the use of the given * Retrieve the next available session tag for identifying the use of the given
* key when communicating with the target. If this returns null, no tags are * key when communicating with the target. If this returns null, no tags are
@ -232,10 +249,8 @@ class TransientSessionKeyManager extends SessionKeyManager {
_log.debug("Tags delivered: " + sessionTags.size() + " for key: " + key.toBase64() + ": " + sessionTags); _log.debug("Tags delivered: " + sessionTags.size() + " for key: " + key.toBase64() + ": " + sessionTags);
} }
OutboundSession sess = getSession(target); OutboundSession sess = getSession(target);
if (sess == null) { if (sess == null)
createSession(target, key); sess = createAndReturnSession(target, key);
sess = getSession(target);
}
sess.setCurrentKey(key); sess.setCurrentKey(key);
TagSet set = new TagSet(sessionTags, key, _context.clock().now()); TagSet set = new TagSet(sessionTags, key, _context.clock().now());
sess.addTags(set); sess.addTags(set);
@ -257,13 +272,13 @@ class TransientSessionKeyManager extends SessionKeyManager {
* *
*/ */
@Override @Override
public void tagsReceived(SessionKey key, Set sessionTags) { public void tagsReceived(SessionKey key, Set<SessionTag> sessionTags) {
int overage = 0; int overage = 0;
TagSet tagSet = new TagSet(sessionTags, key, _context.clock().now()); TagSet tagSet = new TagSet(sessionTags, key, _context.clock().now());
TagSet old = null; TagSet old = null;
SessionTag dupTag = null; SessionTag dupTag = null;
for (Iterator iter = sessionTags.iterator(); iter.hasNext();) { for (Iterator<SessionTag> iter = sessionTags.iterator(); iter.hasNext();) {
SessionTag tag = (SessionTag) iter.next(); SessionTag tag = iter.next();
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Receiving tag " + tag + " for key " + key.toBase64() + " / " + key.toString() + ": tagSet: " + tagSet); _log.debug("Receiving tag " + tag + " for key " + key.toBase64() + " / " + key.toString() + ": tagSet: " + tagSet);
synchronized (_inboundTagSets) { synchronized (_inboundTagSets) {
@ -284,12 +299,12 @@ class TransientSessionKeyManager extends SessionKeyManager {
if (old != null) { if (old != null) {
// drop both old and tagSet tags // drop both old and tagSet tags
synchronized (_inboundTagSets) { synchronized (_inboundTagSets) {
for (Iterator iter = old.getTags().iterator(); iter.hasNext(); ) { for (Iterator<SessionTag> iter = old.getTags().iterator(); iter.hasNext(); ) {
SessionTag tag = (SessionTag)iter.next(); SessionTag tag = iter.next();
_inboundTagSets.remove(tag); _inboundTagSets.remove(tag);
} }
for (Iterator iter = sessionTags.iterator(); iter.hasNext(); ) { for (Iterator<SessionTag> iter = sessionTags.iterator(); iter.hasNext(); ) {
SessionTag tag = (SessionTag)iter.next(); SessionTag tag = iter.next();
_inboundTagSets.remove(tag); _inboundTagSets.remove(tag);
} }
} }
@ -326,10 +341,10 @@ class TransientSessionKeyManager extends SessionKeyManager {
int tags = 0; int tags = 0;
int toRemove = overage * 2; int toRemove = overage * 2;
_log.log(Log.CRIT, "TOO MANY SESSION TAGS! Starting cleanup, overage = " + overage); _log.log(Log.CRIT, "TOO MANY SESSION TAGS! Starting cleanup, overage = " + overage);
List removed = new ArrayList(toRemove); List<TagSet> removed = new ArrayList(toRemove);
synchronized (_inboundTagSets) { synchronized (_inboundTagSets) {
for (Iterator iter = _inboundTagSets.values().iterator(); iter.hasNext(); ) { for (Iterator<TagSet> iter = _inboundTagSets.values().iterator(); iter.hasNext(); ) {
TagSet set = (TagSet)iter.next(); TagSet set = iter.next();
int size = set.getTags().size(); int size = set.getTags().size();
if (size > 1000) if (size > 1000)
absurd++; absurd++;
@ -345,8 +360,8 @@ class TransientSessionKeyManager extends SessionKeyManager {
} }
for (int i = 0; i < removed.size(); i++) { for (int i = 0; i < removed.size(); i++) {
TagSet cur = (TagSet)removed.get(i); TagSet cur = (TagSet)removed.get(i);
for (Iterator iter = cur.getTags().iterator(); iter.hasNext(); ) { for (Iterator<SessionTag> iter = cur.getTags().iterator(); iter.hasNext(); ) {
SessionTag tag = (SessionTag)iter.next(); SessionTag tag = iter.next();
_inboundTagSets.remove(tag); _inboundTagSets.remove(tag);
tags++; tags++;
} }
@ -429,9 +444,9 @@ class TransientSessionKeyManager extends SessionKeyManager {
bufSummary = new StringBuffer(1024); bufSummary = new StringBuffer(1024);
} }
synchronized (_inboundTagSets) { synchronized (_inboundTagSets) {
for (Iterator iter = _inboundTagSets.keySet().iterator(); iter.hasNext();) { for (Iterator<SessionTag> iter = _inboundTagSets.keySet().iterator(); iter.hasNext();) {
SessionTag tag = (SessionTag) iter.next(); SessionTag tag = iter.next();
TagSet ts = (TagSet) _inboundTagSets.get(tag); TagSet ts = _inboundTagSets.get(tag);
long age = now - ts.getDate(); long age = now - ts.getDate();
if (age > SESSION_LIFETIME_MAX_MS) { if (age > SESSION_LIFETIME_MAX_MS) {
//if (ts.getDate() < now - SESSION_LIFETIME_MAX_MS) { //if (ts.getDate() < now - SESSION_LIFETIME_MAX_MS) {
@ -455,9 +470,9 @@ class TransientSessionKeyManager extends SessionKeyManager {
//_log.warn("Expiring tags: [" + tagsToDrop + "]"); //_log.warn("Expiring tags: [" + tagsToDrop + "]");
synchronized (_outboundSessions) { synchronized (_outboundSessions) {
for (Iterator iter = _outboundSessions.keySet().iterator(); iter.hasNext();) { for (Iterator<PublicKey> iter = _outboundSessions.keySet().iterator(); iter.hasNext();) {
PublicKey key = (PublicKey) iter.next(); PublicKey key = iter.next();
OutboundSession sess = (OutboundSession) _outboundSessions.get(key); OutboundSession sess = _outboundSessions.get(key);
removed += sess.expireTags(); removed += sess.expireTags();
if (sess.availableTags() <= 0) { if (sess.availableTags() <= 0) {
iter.remove(); iter.remove();
@ -472,22 +487,22 @@ class TransientSessionKeyManager extends SessionKeyManager {
StringBuffer buf = new StringBuffer(1024); StringBuffer buf = new StringBuffer(1024);
buf.append("<h2>Inbound sessions</h2>"); buf.append("<h2>Inbound sessions</h2>");
buf.append("<table border=\"1\">"); buf.append("<table border=\"1\">");
Set inbound = getInboundTagSets(); Set<TagSet> inbound = getInboundTagSets();
Map inboundSets = new HashMap(inbound.size()); Map<SessionKey, Set<TagSet>> inboundSets = new HashMap(inbound.size());
for (Iterator iter = inbound.iterator(); iter.hasNext();) { for (Iterator<TagSet> iter = inbound.iterator(); iter.hasNext();) {
TagSet ts = (TagSet) iter.next(); TagSet ts = iter.next();
if (!inboundSets.containsKey(ts.getAssociatedKey())) inboundSets.put(ts.getAssociatedKey(), new HashSet()); if (!inboundSets.containsKey(ts.getAssociatedKey())) inboundSets.put(ts.getAssociatedKey(), new HashSet());
Set sets = (Set) inboundSets.get(ts.getAssociatedKey()); Set<TagSet> sets = inboundSets.get(ts.getAssociatedKey());
sets.add(ts); sets.add(ts);
} }
for (Iterator iter = inboundSets.keySet().iterator(); iter.hasNext();) { for (Iterator<SessionKey> iter = inboundSets.keySet().iterator(); iter.hasNext();) {
SessionKey skey = (SessionKey) iter.next(); SessionKey skey = iter.next();
Set sets = (Set) inboundSets.get(skey); Set<TagSet> sets = inboundSets.get(skey);
buf.append("<tr><td><b>Session key</b>: ").append(skey.toBase64()).append("</td>"); buf.append("<tr><td><b>Session key</b>: ").append(skey.toBase64()).append("</td>");
buf.append("<td><b># Sets:</b> ").append(sets.size()).append("</td></tr>"); buf.append("<td><b># Sets:</b> ").append(sets.size()).append("</td></tr>");
buf.append("<tr><td colspan=\"2\"><ul>"); buf.append("<tr><td colspan=\"2\"><ul>");
for (Iterator siter = sets.iterator(); siter.hasNext();) { for (Iterator<TagSet> siter = sets.iterator(); siter.hasNext();) {
TagSet ts = (TagSet) siter.next(); TagSet ts = siter.next();
buf.append("<li><b>Received on:</b> ").append(new Date(ts.getDate())).append(" with ") buf.append("<li><b>Received on:</b> ").append(new Date(ts.getDate())).append(" with ")
.append(ts.getTags().size()).append(" tags remaining</li>"); .append(ts.getTags().size()).append(" tags remaining</li>");
} }
@ -498,17 +513,17 @@ class TransientSessionKeyManager extends SessionKeyManager {
buf.append("<h2><b>Outbound sessions</b></h2>"); buf.append("<h2><b>Outbound sessions</b></h2>");
buf.append("<table border=\"1\">"); buf.append("<table border=\"1\">");
Set outbound = getOutboundSessions(); Set<OutboundSession> outbound = getOutboundSessions();
for (Iterator iter = outbound.iterator(); iter.hasNext();) { for (Iterator<OutboundSession> iter = outbound.iterator(); iter.hasNext();) {
OutboundSession sess = (OutboundSession) iter.next(); OutboundSession sess = iter.next();
buf.append("<tr><td><b>Target key:</b> ").append(sess.getTarget().toString()).append("<br />"); buf.append("<tr><td><b>Target key:</b> ").append(sess.getTarget().toString()).append("<br />");
buf.append("<b>Established:</b> ").append(new Date(sess.getEstablishedDate())).append("<br />"); buf.append("<b>Established:</b> ").append(new Date(sess.getEstablishedDate())).append("<br />");
buf.append("<b>Last Used:</b> ").append(new Date(sess.getLastUsedDate())).append("<br />"); buf.append("<b>Last Used:</b> ").append(new Date(sess.getLastUsedDate())).append("<br />");
buf.append("<b># Sets:</b> ").append(sess.getTagSets().size()).append("</td></tr>"); buf.append("<b># Sets:</b> ").append(sess.getTagSets().size()).append("</td></tr>");
buf.append("<tr><td><b>Session key:</b> ").append(sess.getCurrentKey().toBase64()).append("</td></tr>"); buf.append("<tr><td><b>Session key:</b> ").append(sess.getCurrentKey().toBase64()).append("</td></tr>");
buf.append("<tr><td><ul>"); buf.append("<tr><td><ul>");
for (Iterator siter = sess.getTagSets().iterator(); siter.hasNext();) { for (Iterator<TagSet> siter = sess.getTagSets().iterator(); siter.hasNext();) {
TagSet ts = (TagSet) siter.next(); TagSet ts = siter.next();
buf.append("<li><b>Sent on:</b> ").append(new Date(ts.getDate())).append(" with ").append( buf.append("<li><b>Sent on:</b> ").append(new Date(ts.getDate())).append(" with ").append(
ts.getTags() ts.getTags()
.size()) .size())
@ -526,13 +541,13 @@ class TransientSessionKeyManager extends SessionKeyManager {
private SessionKey _currentKey; private SessionKey _currentKey;
private long _established; private long _established;
private long _lastUsed; private long _lastUsed;
private List _tagSets; private List<TagSet> _tagSets;
public OutboundSession(PublicKey target) { public OutboundSession(PublicKey target) {
this(target, null, _context.clock().now(), _context.clock().now(), new ArrayList()); this(target, null, _context.clock().now(), _context.clock().now(), new ArrayList());
} }
OutboundSession(PublicKey target, SessionKey curKey, long established, long lastUsed, List tagSets) { OutboundSession(PublicKey target, SessionKey curKey, long established, long lastUsed, List<TagSet> tagSets) {
_target = target; _target = target;
_currentKey = curKey; _currentKey = curKey;
_established = established; _established = established;
@ -541,7 +556,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
} }
/** list of TagSet objects */ /** list of TagSet objects */
List getTagSets() { List<TagSet> getTagSets() {
synchronized (_tagSets) { synchronized (_tagSets) {
return new ArrayList(_tagSets); return new ArrayList(_tagSets);
} }
@ -560,7 +575,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
if (_currentKey != null) { if (_currentKey != null) {
if (!_currentKey.equals(key)) { if (!_currentKey.equals(key)) {
int dropped = 0; int dropped = 0;
List sets = _tagSets; List<TagSet> sets = _tagSets;
_tagSets = new ArrayList(); _tagSets = new ArrayList();
for (int i = 0; i < sets.size(); i++) { for (int i = 0; i < sets.size(); i++) {
TagSet set = (TagSet) sets.get(i); TagSet set = (TagSet) sets.get(i);
@ -642,8 +657,8 @@ class TransientSessionKeyManager extends SessionKeyManager {
public long getLastExpirationDate() { public long getLastExpirationDate() {
long last = 0; long last = 0;
synchronized (_tagSets) { synchronized (_tagSets) {
for (Iterator iter = _tagSets.iterator(); iter.hasNext();) { for (Iterator<TagSet> iter = _tagSets.iterator(); iter.hasNext();) {
TagSet set = (TagSet) iter.next(); TagSet set = iter.next();
if ( (set.getDate() > last) && (set.getTags().size() > 0) ) if ( (set.getDate() > last) && (set.getTags().size() > 0) )
last = set.getDate(); last = set.getDate();
} }
@ -663,12 +678,12 @@ class TransientSessionKeyManager extends SessionKeyManager {
} }
static class TagSet { static class TagSet {
private Set _sessionTags; private Set<SessionTag> _sessionTags;
private SessionKey _key; private SessionKey _key;
private long _date; private long _date;
private Exception _createdBy; private Exception _createdBy;
public TagSet(Set tags, SessionKey key, long date) { public TagSet(Set<SessionTag> tags, SessionKey key, long date) {
if (key == null) throw new IllegalArgumentException("Missing key"); if (key == null) throw new IllegalArgumentException("Missing key");
if (tags == null) throw new IllegalArgumentException("Missing tags"); if (tags == null) throw new IllegalArgumentException("Missing tags");
_sessionTags = tags; _sessionTags = tags;
@ -692,7 +707,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
} }
/** tags still available */ /** tags still available */
public Set getTags() { public Set<SessionTag> getTags() {
return _sessionTags; return _sessionTags;
} }

View File

@ -57,7 +57,7 @@ public class Router {
private RouterInfo _routerInfo; private RouterInfo _routerInfo;
private long _started; private long _started;
private boolean _higherVersionSeen; private boolean _higherVersionSeen;
private SessionKeyPersistenceHelper _sessionKeyPersistenceHelper; //private SessionKeyPersistenceHelper _sessionKeyPersistenceHelper;
private boolean _killVMOnEnd; private boolean _killVMOnEnd;
private boolean _isAlive; private boolean _isAlive;
private int _gracefulExitCode; private int _gracefulExitCode;
@ -144,7 +144,7 @@ public class Router {
_higherVersionSeen = false; _higherVersionSeen = false;
_log = _context.logManager().getLog(Router.class); _log = _context.logManager().getLog(Router.class);
_log.info("New router created with config file " + _configFilename); _log.info("New router created with config file " + _configFilename);
_sessionKeyPersistenceHelper = new SessionKeyPersistenceHelper(_context); //_sessionKeyPersistenceHelper = new SessionKeyPersistenceHelper(_context);
_killVMOnEnd = true; _killVMOnEnd = true;
_oomListener = new I2PThread.OOMEventListener() { _oomListener = new I2PThread.OOMEventListener() {
public void outOfMemory(OutOfMemoryError oom) { public void outOfMemory(OutOfMemoryError oom) {
@ -261,7 +261,7 @@ public class Router {
SimpleScheduler.getInstance().addPeriodicEvent(new CoalesceStatsEvent(_context), 20*1000); SimpleScheduler.getInstance().addPeriodicEvent(new CoalesceStatsEvent(_context), 20*1000);
_context.jobQueue().addJob(new UpdateRoutingKeyModifierJob(_context)); _context.jobQueue().addJob(new UpdateRoutingKeyModifierJob(_context));
warmupCrypto(); warmupCrypto();
_sessionKeyPersistenceHelper.startup(); //_sessionKeyPersistenceHelper.startup();
//_context.adminManager().startup(); //_context.adminManager().startup();
_context.blocklist().startup(); _context.blocklist().startup();
@ -813,7 +813,7 @@ public class Router {
try { _context.messageRegistry().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the message registry", t); } try { _context.messageRegistry().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the message registry", t); }
try { _context.messageValidator().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the message validator", t); } try { _context.messageValidator().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the message validator", t); }
try { _context.inNetMessagePool().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the inbound net pool", t); } try { _context.inNetMessagePool().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the inbound net pool", t); }
try { _sessionKeyPersistenceHelper.shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the session key manager", t); } //try { _sessionKeyPersistenceHelper.shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the session key manager", t); }
RouterContext.listContexts().remove(_context); RouterContext.listContexts().remove(_context);
dumpStats(); dumpStats();
finalShutdown(exitCode); finalShutdown(exitCode);

View File

@ -1,115 +0,0 @@
package net.i2p.router;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Writer;
import net.i2p.crypto.PersistentSessionKeyManager;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.util.Log;
/**
* Centralize the sessionKeyManager persistence (rather than leave it to a private
* job in the startup job)
*
*/
public class SessionKeyPersistenceHelper implements Service {
private Log _log;
private RouterContext _context;
private SessionKeyWriterJob _writerJob;
private final static long PERSIST_DELAY = 3*60*1000;
private final static String PROP_SESSION_KEY_FILE = "router.sessionKeys.location";
private final static String DEFAULT_SESSION_KEY_FILE = "sessionKeys.dat";
public SessionKeyPersistenceHelper(RouterContext context) {
_context = context;
_log = _context.logManager().getLog(SessionKeyPersistenceHelper.class);
_writerJob = new SessionKeyWriterJob();
}
public void shutdown() {
writeState();
}
public void restart() {
writeState();
startup();
}
private String getKeyFile() {
String val = _context.router().getConfigSetting(PROP_SESSION_KEY_FILE);
if (val == null)
val = DEFAULT_SESSION_KEY_FILE;
return val;
}
public void startup() {
SessionKeyManager mgr = _context.sessionKeyManager();
if (mgr instanceof PersistentSessionKeyManager) {
PersistentSessionKeyManager manager = (PersistentSessionKeyManager)mgr;
File f = new File(getKeyFile());
if (f.exists()) {
FileInputStream fin = null;
try {
fin = new FileInputStream(f);
manager.loadState(fin);
int expired = manager.aggressiveExpire();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Session keys loaded [not error] with " + expired
+ " sets immediately expired");
} catch (Throwable t) {
_log.error("Error reading in session key data", t);
} finally {
if (fin != null) try { fin.close(); } catch (IOException ioe) {}
}
}
_context.jobQueue().addJob(_writerJob);
}
}
private void writeState() {
if (true) return;
Object o = _context.sessionKeyManager();
if (!(o instanceof PersistentSessionKeyManager)) {
_log.error("Unable to persist the session key state - manager is " + o.getClass().getName());
return;
}
PersistentSessionKeyManager mgr = (PersistentSessionKeyManager)o;
// only need for synchronization is during shutdown()
synchronized (mgr) {
FileOutputStream fos = null;
try {
int expired = mgr.aggressiveExpire();
if (expired > 0) {
_log.info("Agressive expired " + expired + " tag sets");
}
fos = new FileOutputStream(getKeyFile());
mgr.saveState(fos);
fos.flush();
_log.debug("Session keys written");
} catch (Throwable t) {
_log.debug("Error writing session key state", t);
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
}
}
public void renderStatusHTML(Writer out) { }
private class SessionKeyWriterJob extends JobImpl {
public SessionKeyWriterJob() {
super(SessionKeyPersistenceHelper.this._context);
getTiming().setStartAfter(PERSIST_DELAY);
}
public String getName() { return "Write Session Keys"; }
public void runJob() {
writeState();
requeue(PERSIST_DELAY);
}
}
}