I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects
Commit 09cf9737 authored by zzz's avatar zzz
Browse files

BuildHandler: Enforce request record timestamp

BuildRequestor: Randomize timestamp to prevent hop ID at top of hour
parent 5af749a2
No related branches found
No related tags found
No related merge requests found
......@@ -146,10 +146,10 @@ public class BuildRequestRecord {
return (_data.getData()[_data.getOffset() + OFF_FLAG] & FLAG_OUTBOUND_ENDPOINT) != 0;
}
/**
* Time that the request was sent, truncated to the nearest hour
* Time that the request was sent (ms), truncated to the nearest hour
*/
public long readRequestTime() {
return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_REQ_TIME, 4) * 60l * 60l * 1000l;
return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_REQ_TIME, 4) * (60 * 60 * 1000L);
}
/**
* What message ID should we send the request to the next hop with. If this is the outbound tunnel endpoint,
......@@ -250,6 +250,8 @@ public class BuildRequestRecord {
else if (isOutEndpoint)
buf[OFF_FLAG] |= FLAG_OUTBOUND_ENDPOINT;
long truncatedHour = ctx.clock().now();
// prevent hop identification at top of the hour
truncatedHour -= ctx.random().nextInt(90*1000);
truncatedHour /= (60l*60l*1000l);
DataHelper.toLong(buf, OFF_REQ_TIME, 4, truncatedHour);
DataHelper.toLong(buf, OFF_SEND_MSG_ID, 4, nextMsgId);
......
......@@ -85,6 +85,11 @@ class BuildHandler implements Runnable {
*/
private static final int NEXT_HOP_SEND_TIMEOUT = 25*1000;
private static final long MAX_REQUEST_FUTURE = 5*60*1000;
/** must be > 1 hour due to rouding down */
private static final long MAX_REQUEST_AGE = 65*60*1000;
public BuildHandler(RouterContext ctx, TunnelPoolManager manager, BuildExecutor exec) {
_context = ctx;
_log = ctx.logManager().getLog(getClass());
......@@ -101,8 +106,10 @@ class BuildHandler implements Runnable {
_context.statManager().createRateStat("tunnel.reject.50", "How often we reject a tunnel because of a critical issue (shutdown, etc)", "Tunnels", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRequiredRateStat("tunnel.decryptRequestTime", "Time to decrypt a build request (ms)", "Tunnels", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRequiredRateStat("tunnel.rejectTimeout", "Reject tunnel count (unknown next hop)", "Tunnels", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRequiredRateStat("tunnel.rejectTimeout2", "Reject tunnel count (can't contact next hop)", "Tunnels", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRateStat("tunnel.rejectTooOld", "Reject tunnel count (too old)", "Tunnels", new long[] { 3*60*60*1000 });
_context.statManager().createRateStat("tunnel.rejectFuture", "Reject tunnel count (time in future)", "Tunnels", new long[] { 3*60*60*1000 });
_context.statManager().createRateStat("tunnel.rejectTimeout", "Reject tunnel count (unknown next hop)", "Tunnels", new long[] { 60*60*1000 });
_context.statManager().createRateStat("tunnel.rejectTimeout2", "Reject tunnel count (can't contact next hop)", "Tunnels", new long[] { 60*60*1000 });
_context.statManager().createRequiredRateStat("tunnel.rejectDupID", "Part. tunnel dup ID", "Tunnels", new long[] { 24*60*60*1000 });
_context.statManager().createRequiredRateStat("tunnel.ownDupID", "Our tunnel dup. ID", "Tunnels", new long[] { 24*60*60*1000 });
_context.statManager().createRequiredRateStat("tunnel.rejectHostile", "Reject malicious tunnel", "Tunnels", new long[] { 24*60*60*1000 });
......@@ -587,11 +594,24 @@ class BuildHandler implements Runnable {
}
}
// time is in hours, and only for log below - what's the point?
// tunnel-alt-creation.html specifies that this is enforced +/- 1 hour but it is not.
// time is in hours, rounded down.
// tunnel-alt-creation.html specifies that this is enforced +/- 1 hour but it was not.
// As of 0.9.16, allow + 5 minutes to - 65 minutes.
long time = req.readRequestTime();
long now = (_context.clock().now() / (60l*60l*1000l)) * (60*60*1000);
int ourSlot = -1;
long timeDiff = now - time;
if (timeDiff > MAX_REQUEST_AGE) {
_context.statManager().addRateData("tunnel.rejectTooOld", 1);
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping build request too old... replay attack? " + DataHelper.formatDuration(timeDiff));
return;
}
if (timeDiff < 0 - MAX_REQUEST_FUTURE) {
_context.statManager().addRateData("tunnel.rejectFuture", 1);
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping build request too far in future " + DataHelper.formatDuration(0 - timeDiff));
return;
}
int response;
if (_context.router().isHidden()) {
......@@ -764,6 +784,7 @@ class BuildHandler implements Runnable {
byte reply[] = BuildResponseRecord.create(_context, response, req.readReplyKey(), req.readReplyIV(), state.msg.getUniqueId());
int records = state.msg.getRecordCount();
int ourSlot = -1;
for (int j = 0; j < records; j++) {
if (state.msg.getRecord(j) == null) {
ourSlot = j;
......@@ -780,7 +801,7 @@ class BuildHandler implements Runnable {
+ " accepted? " + response + " receiving on " + ourId
+ " sending to " + nextId
+ " on " + nextPeer
+ " inGW? " + isInGW + " outEnd? " + isOutEnd + " time difference " + (now-time)
+ " inGW? " + isInGW + " outEnd? " + isOutEnd
+ " recvDelay " + recvDelay + " replyMessage " + req.readReplyMessageId()
+ " replyKey " + req.readReplyKey() + " replyIV " + Base64.encode(req.readReplyIV()));
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment