Router: Implement expiration for BlindData entries

This commit is contained in:
zzz
2019-09-17 11:17:14 +00:00
parent 0c2a8e9244
commit 94c96b09e9
8 changed files with 99 additions and 24 deletions

View File

@ -63,7 +63,7 @@ public abstract class LocalHTTPServer {
"Add to addressbook failed - bad parameters"; "Add to addressbook failed - bad parameters";
private final static String ERR_B32 = private final static String ERR_B32 =
"HTTP/1.1 409 Bad\r\n"+ "HTTP/1.1 400 Bad\r\n"+
"Content-Type: text/plain\r\n"+ "Content-Type: text/plain\r\n"+
"Connection: close\r\n"+ "Connection: close\r\n"+
"Proxy-Connection: close\r\n"+ "Proxy-Connection: close\r\n"+
@ -271,8 +271,13 @@ public abstract class LocalHTTPServer {
SigningPublicKey spk = bd.getUnblindedPubKey(); SigningPublicKey spk = bd.getUnblindedPubKey();
SigType bt = bd.getBlindedSigType(); SigType bt = bd.getBlindedSigType();
bd = new BlindData(context, spk, bt, secret, authType, privateKey); bd = new BlindData(context, spk, bt, secret, authType, privateKey);
long now = context.clock().now();
bd.setDate(now);
long exp = now + ((bd.getAuthRequired() || bd.getSecretRequired()) ? 365*24*60*60*1000L
: 90*24*68*60*1000L);
bd.setExpiration(exp);
I2PSession sess = sockMgr.getSession(); I2PSession sess = sockMgr.getSession();
sess.sendBlindingInfo(bd, 365*60*60*1000); sess.sendBlindingInfo(bd);
writeRedirectPage(out, success, host, "FIXME", url); writeRedirectPage(out, success, host, "FIXME", url);
return; return;
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {

View File

@ -439,10 +439,9 @@ public interface I2PSession {
/** /**
* *
* @param expiration ms from now, 0 means forever
* @since 0.9.43 * @since 0.9.43
*/ */
public void sendBlindingInfo(BlindData bd, int expiration) throws I2PSessionException; public void sendBlindingInfo(BlindData bd) throws I2PSessionException;
/** /**
* Listen on specified protocol and port. * Listen on specified protocol and port.

View File

@ -1860,10 +1860,9 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
/** /**
* *
* @param expiration ms from now, 0 means forever
* @since 0.9.43 * @since 0.9.43
*/ */
public void sendBlindingInfo(BlindData bd, int expiration) throws I2PSessionException { public void sendBlindingInfo(BlindData bd) throws I2PSessionException {
if (!_routerSupportsBlindingInfo) if (!_routerSupportsBlindingInfo)
throw new I2PSessionException("Router does not support BlindingInfo"); throw new I2PSessionException("Router does not support BlindingInfo");
if (_log.shouldInfo()) if (_log.shouldInfo())
@ -1871,7 +1870,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
SessionId id = _sessionId; SessionId id = _sessionId;
if (id == null) if (id == null)
id = DUMMY_SESSION; id = DUMMY_SESSION;
sendMessage_unchecked(new BlindingInfoMessage(bd, id, expiration)); sendMessage_unchecked(new BlindingInfoMessage(bd, id));
} }
protected void updateActivity() { protected void updateActivity() {

View File

@ -28,6 +28,7 @@ public class BlindData {
private boolean _secretRequired; private boolean _secretRequired;
private boolean _authRequired; private boolean _authRequired;
private long _date; private long _date;
private long _expiration;
private String _b32; private String _b32;
/** /**
@ -257,6 +258,7 @@ public class BlindData {
} }
/** /**
* Creation date. Absolute timestamp.
* @since 0.9.41 * @since 0.9.41
*/ */
public void setDate(long date) { public void setDate(long date) {
@ -264,6 +266,9 @@ public class BlindData {
} }
/** /**
* Creation date. Absolute timestamp.
* Returns zero if not specified.
*
* @return creation date or as overridden by setDate() * @return creation date or as overridden by setDate()
* @since 0.9.41 * @since 0.9.41
*/ */
@ -271,14 +276,33 @@ public class BlindData {
return _date; return _date;
} }
/**
* Expiration date. Absolute timestamp.
* @since 0.9.43
*/
public void setExpiration(long date) {
_expiration = date;
}
/**
* Expiration date. Absolute timestamp.
* Returns zero if not specified.
*
* @return expiration date or as overridden by setExpiration()
* @since 0.9.43
*/
public long getExpiration() {
return _expiration;
}
@Override @Override
public synchronized String toString() { public synchronized String toString() {
calculate(); calculate();
StringBuilder buf = new StringBuilder(1024); StringBuilder buf = new StringBuilder(1024);
buf.append("[BlindData: "); buf.append("[BlindData: ");
buf.append("\n\tSigningPublicKey: ").append(_clearSPK); buf.append("\n\tSigningPublicKey: ").append(_clearSPK);
buf.append("\n\tAlpha : ").append(_alpha); buf.append("\n\tAlpha : ").append(getAlpha());
buf.append("\n\tAlpha valid for : ").append((new Date(_date)).toString()); buf.append("\n\tAlpha valid for : ").append((new Date(_routingKeyGenMod)).toString());
buf.append("\n\tBlindedPublicKey: ").append(_blindSPK); buf.append("\n\tBlindedPublicKey: ").append(_blindSPK);
buf.append("\n\tBlinded Hash : ").append(_blindHash); buf.append("\n\tBlinded Hash : ").append(_blindHash);
if (_secret != null) if (_secret != null)
@ -305,6 +329,10 @@ public class BlindData {
buf.append("\n\t + secret : ").append(Blinding.encode(_clearSPK, true, _authRequired)); buf.append("\n\t + secret : ").append(Blinding.encode(_clearSPK, true, _authRequired));
if (!(_authRequired || _secretRequired)) if (!(_authRequired || _secretRequired))
buf.append("\n\t + auth,secret : ").append(Blinding.encode(_clearSPK, true, true)); buf.append("\n\t + auth,secret : ").append(Blinding.encode(_clearSPK, true, true));
if (_date > 0)
buf.append("\n\tCreated : ").append((new Date(_date)).toString());
if (_expiration > 0)
buf.append("\n\tExpires : ").append((new Date(_expiration)).toString());
buf.append(']'); buf.append(']');
return buf.toString(); return buf.toString();
} }

View File

@ -60,9 +60,8 @@ public class BlindingInfoMessage extends I2CPMessageImpl {
* *
* @param expiration ms from now or 0 for forever * @param expiration ms from now or 0 for forever
*/ */
public BlindingInfoMessage(BlindData bd, public BlindingInfoMessage(BlindData bd, SessionId id) {
SessionId id, int expiration) { this(id, bd.getExpiration(), bd.getAuthType(), bd.getBlindedSigType(), bd.getAuthPrivKey(), bd.getSecret());
this(id, expiration, bd.getAuthType(), bd.getBlindedSigType(), bd.getAuthPrivKey(), bd.getSecret());
Destination dest = bd.getDestination(); Destination dest = bd.getDestination();
if (dest != null) { if (dest != null) {
_dest = dest; _dest = dest;
@ -157,13 +156,13 @@ public class BlindingInfoMessage extends I2CPMessageImpl {
_endpointType = TYPE_KEY; _endpointType = TYPE_KEY;
} }
private BlindingInfoMessage(SessionId id, int expiration, int authType, SigType blindType, private BlindingInfoMessage(SessionId id, long expiration, int authType, SigType blindType,
PrivateKey privKey, String secret) { PrivateKey privKey, String secret) {
if (id == null || blindType == null) if (id == null || blindType == null)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if (authType != BlindData.AUTH_NONE && authType != BlindData.AUTH_DH && if (authType != BlindData.AUTH_NONE && authType != BlindData.AUTH_DH &&
authType != BlindData.AUTH_PSK) authType != BlindData.AUTH_PSK)
throw new IllegalArgumentException(); throw new IllegalArgumentException("Bad auth type");
if (authType == BlindData.AUTH_NONE && privKey != null) if (authType == BlindData.AUTH_NONE && privKey != null)
throw new IllegalArgumentException("no key required"); throw new IllegalArgumentException("no key required");
if (authType != BlindData.AUTH_NONE && privKey == null) if (authType != BlindData.AUTH_NONE && privKey == null)
@ -171,8 +170,9 @@ public class BlindingInfoMessage extends I2CPMessageImpl {
_sessionId = id; _sessionId = id;
_authType = authType; _authType = authType;
_blindType = blindType; _blindType = blindType;
if (expiration > 0) _expiration = expiration;
_expiration = expiration + I2PAppContext.getGlobalContext().clock().now(); if (expiration > 0 && expiration < Integer.MAX_VALUE)
_expiration += I2PAppContext.getGlobalContext().clock().now();
_privkey = privKey; _privkey = privKey;
_secret = secret; _secret = secret;
} }
@ -262,6 +262,10 @@ public class BlindingInfoMessage extends I2CPMessageImpl {
_blindData = new BlindData(I2PAppContext.getGlobalContext(), _dest, _blindType, _secret, _authType, _privkey); _blindData = new BlindData(I2PAppContext.getGlobalContext(), _dest, _blindType, _secret, _authType, _privkey);
else if (_endpointType == TYPE_KEY) else if (_endpointType == TYPE_KEY)
_blindData = new BlindData(I2PAppContext.getGlobalContext(), _pubkey, _blindType, _secret, _authType, _privkey); _blindData = new BlindData(I2PAppContext.getGlobalContext(), _pubkey, _blindType, _secret, _authType, _privkey);
if (_blindData != null) {
_blindData.setDate(I2PAppContext.getGlobalContext().clock().now());
_blindData.setExpiration(_expiration);
}
// HASH and HOST not supported by router yet // HASH and HOST not supported by router yet
return _blindData; return _blindData;
} }
@ -278,7 +282,7 @@ public class BlindingInfoMessage extends I2CPMessageImpl {
_blindType = SigType.getByCode(bt); _blindType = SigType.getByCode(bt);
if (_blindType == null) if (_blindType == null)
throw new I2CPMessageException("unsupported sig type " + bt); throw new I2CPMessageException("unsupported sig type " + bt);
_expiration = DataHelper.readLong(in, 4); _expiration = DataHelper.readLong(in, 4) * 1000;
if (_endpointType == TYPE_HASH) { if (_endpointType == TYPE_HASH) {
_hash = Hash.create(in); _hash = Hash.create(in);
} else if (_endpointType == TYPE_HOST) { } else if (_endpointType == TYPE_HOST) {
@ -338,7 +342,7 @@ public class BlindingInfoMessage extends I2CPMessageImpl {
os.write(flags); os.write(flags);
os.write((byte) _endpointType); os.write((byte) _endpointType);
DataHelper.writeLong(os, 2, _blindType.getCode()); DataHelper.writeLong(os, 2, _blindType.getCode());
DataHelper.writeLong(os, 4, _expiration); DataHelper.writeLong(os, 4, _expiration / 1000);
if (_endpointType == TYPE_HASH) { if (_endpointType == TYPE_HASH) {
_hash.writeBytes(os); _hash.writeBytes(os);
} else if (_endpointType == TYPE_HOST) { } else if (_endpointType == TYPE_HOST) {

View File

@ -838,10 +838,18 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
_context.netDb().setBlindData(bd); _context.netDb().setBlindData(bd);
if (_log.shouldWarn()) if (_log.shouldWarn())
_log.warn("Updated: " + bd); _log.warn("Updated: " + bd);
} else {
long oexp = obd.getExpiration();
long nexp = bd.getExpiration();
if (nexp > oexp) {
obd.setExpiration(nexp);
if (_log.shouldWarn())
_log.warn("Updated expiration: " + obd);
} else { } else {
if (_log.shouldWarn()) if (_log.shouldWarn())
_log.warn("No change: " + obd); _log.warn("No change: " + obd);
} }
} }
} }
}
} }

View File

@ -105,6 +105,11 @@ class LookupDestJob extends JobImpl {
bd = bd2; bd = bd2;
} }
} else { } else {
long now = getContext().clock().now();
bd.setDate(now);
long exp = now + ((bd.getAuthRequired() || bd.getSecretRequired()) ? 365*24*60*60*1000L
: 90*24*68*60*1000L);
bd.setExpiration(exp);
getContext().netDb().setBlindData(bd); getContext().netDb().setBlindData(bd);
} }
h = bd.getBlindedHash(); h = bd.getBlindedHash();

View File

@ -275,13 +275,17 @@ class BlindCache {
/** /**
* Load from file. * Load from file.
* Format: * Format:
* sigtype,bsigtype,b64 pubkey,[b64 secret],[b64 dest] * sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest]
*
* If timestamp is positive, it's a creation date;
* if negative, it's a negative expiration date.
*/ */
private synchronized void load() { private synchronized void load() {
File file = new File(_context.getConfigDir(), PERSIST_FILE); File file = new File(_context.getConfigDir(), PERSIST_FILE);
if (!file.exists()) if (!file.exists())
return; return;
Log log = _context.logManager().getLog(BlindCache.class); Log log = _context.logManager().getLog(BlindCache.class);
long now = _context.clock().now();
int count = 0; int count = 0;
BufferedReader br = null; BufferedReader br = null;
try { try {
@ -292,7 +296,14 @@ class BlindCache {
if (line.startsWith("#")) if (line.startsWith("#"))
continue; continue;
try { try {
storeInCache(fromPersistentString(line)); BlindData bd = fromPersistentString(line);
long exp = bd.getExpiration();
if (exp > 0 && exp < now) {
if (log.shouldInfo())
log.info("Skipping expired entry " + bd);
continue;
}
storeInCache(bd);
count++; count++;
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
if (log.shouldLog(Log.WARN)) if (log.shouldLog(Log.WARN))
@ -341,6 +352,9 @@ class BlindCache {
/** /**
* Format: * Format:
* sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest] * sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest]
*
* If timestamp is positive, it's a creation date;
* if negative, it's a negative expiration date.
*/ */
private BlindData fromPersistentString(String line) throws DataFormatException { private BlindData fromPersistentString(String line) throws DataFormatException {
String[] ss = DataHelper.split(line, ",", 8); String[] ss = DataHelper.split(line, ",", 8);
@ -389,13 +403,21 @@ class BlindCache {
} else { } else {
rv = new BlindData(_context, spk, st2, secret, auth, privkey); rv = new BlindData(_context, spk, st2, secret, auth, privkey);
} }
if (time >= 0) {
rv.setDate(time); rv.setDate(time);
} else {
rv.setDate(_context.clock().now());
rv.setExpiration(0 - time);
}
return rv; return rv;
} }
/** /**
* Format: * Format:
* sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest] * sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest]
*
* If timestamp is positive, it's a creation date;
* if negative, it's a negative expiration date.
*/ */
private static String toPersistentString(BlindData bd) { private static String toPersistentString(BlindData bd) {
StringBuilder buf = new StringBuilder(1024); StringBuilder buf = new StringBuilder(1024);
@ -403,7 +425,12 @@ class BlindCache {
buf.append(spk.getType().getCode()).append(','); buf.append(spk.getType().getCode()).append(',');
buf.append(bd.getBlindedSigType().getCode()).append(','); buf.append(bd.getBlindedSigType().getCode()).append(',');
buf.append(bd.getAuthType()).append(','); buf.append(bd.getAuthType()).append(',');
buf.append(bd.getDate()).append(','); long time = bd.getExpiration();
if (time > 0)
time = 0 - time;
else
time = bd.getDate();
buf.append(time).append(',');
buf.append(spk.toBase64()).append(','); buf.append(spk.toBase64()).append(',');
String secret = bd.getSecret(); String secret = bd.getSecret();
if (secret != null && secret.length() > 0) if (secret != null && secret.length() > 0)