forked from I2P_Developers/i2p.i2p
Streaming: Support offline signatures (proposal 123)
Don't send FROM in RESET, not required since 0.9.20 Send RESET when SYN signature verification fails Use cached buffers for signature verification Move setOptionalFrom() from Packet to PacketLocal Always verify packets with signatures, even if not required AIOOBE checks cleanups, log tweaks
This commit is contained in:
@ -15,6 +15,7 @@ import net.i2p.client.I2PSession;
|
|||||||
import net.i2p.client.streaming.I2PSocketException;
|
import net.i2p.client.streaming.I2PSocketException;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
import net.i2p.util.SimpleTimer;
|
import net.i2p.util.SimpleTimer;
|
||||||
import net.i2p.util.SimpleTimer2;
|
import net.i2p.util.SimpleTimer2;
|
||||||
@ -30,6 +31,7 @@ class Connection {
|
|||||||
private final ConnectionManager _connectionManager;
|
private final ConnectionManager _connectionManager;
|
||||||
private final I2PSession _session;
|
private final I2PSession _session;
|
||||||
private Destination _remotePeer;
|
private Destination _remotePeer;
|
||||||
|
private SigningPublicKey _transientSPK;
|
||||||
private final AtomicLong _sendStreamId = new AtomicLong();
|
private final AtomicLong _sendStreamId = new AtomicLong();
|
||||||
private final AtomicLong _receiveStreamId = new AtomicLong();
|
private final AtomicLong _receiveStreamId = new AtomicLong();
|
||||||
private volatile long _lastSendTime;
|
private volatile long _lastSendTime;
|
||||||
@ -327,8 +329,9 @@ class Connection {
|
|||||||
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
|
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
|
||||||
reply.setSendStreamId(_sendStreamId.get());
|
reply.setSendStreamId(_sendStreamId.get());
|
||||||
reply.setReceiveStreamId(_receiveStreamId.get());
|
reply.setReceiveStreamId(_receiveStreamId.get());
|
||||||
// TODO remove this someday, as of 0.9.20 we do not require it
|
// As of 0.9.20 we do not require FROM
|
||||||
reply.setOptionalFrom();
|
// Removed in 0.9.39
|
||||||
|
//reply.setOptionalFrom();
|
||||||
reply.setLocalPort(_localPort);
|
reply.setLocalPort(_localPort);
|
||||||
reply.setRemotePort(_remotePort);
|
reply.setRemotePort(_remotePort);
|
||||||
// this just sends the packet - no retries or whatnot
|
// this just sends the packet - no retries or whatnot
|
||||||
@ -872,6 +875,35 @@ class Connection {
|
|||||||
// now that we know who the other end is, get the rtt etc. from the cache
|
// now that we know who the other end is, get the rtt etc. from the cache
|
||||||
_connectionManager.updateOptsFromShare(this);
|
_connectionManager.updateOptsFromShare(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key to verify signatures with.
|
||||||
|
* The transient SPK if previously received,
|
||||||
|
* else getRemotePeer().getSigningPublicKey() if previously received,
|
||||||
|
* else null.
|
||||||
|
*
|
||||||
|
* @return peer Destination or null if unset
|
||||||
|
* @since 0.9.39
|
||||||
|
*/
|
||||||
|
public synchronized SigningPublicKey getRemoteSPK() {
|
||||||
|
if (_transientSPK != null)
|
||||||
|
return _transientSPK;
|
||||||
|
if (_remotePeer != null)
|
||||||
|
return _remotePeer.getSigningPublicKey();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param transientSPK null ok
|
||||||
|
* @since 0.9.39
|
||||||
|
*/
|
||||||
|
public void setRemoteTransientSPK(SigningPublicKey transientSPK) {
|
||||||
|
synchronized(this) {
|
||||||
|
if (_transientSPK != null)
|
||||||
|
throw new RuntimeException("Remote SPK already set");
|
||||||
|
_transientSPK = transientSPK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What stream do we send data to the peer on?
|
* What stream do we send data to the peer on?
|
||||||
|
@ -7,7 +7,9 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.client.streaming.RouterRestartException;
|
import net.i2p.client.streaming.RouterRestartException;
|
||||||
|
import net.i2p.data.ByteArray;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.util.ByteCache;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
import net.i2p.util.SimpleTimer;
|
import net.i2p.util.SimpleTimer;
|
||||||
import net.i2p.util.SimpleTimer2;
|
import net.i2p.util.SimpleTimer2;
|
||||||
@ -23,6 +25,7 @@ import net.i2p.util.SimpleTimer2;
|
|||||||
class ConnectionHandler {
|
class ConnectionHandler {
|
||||||
private final I2PAppContext _context;
|
private final I2PAppContext _context;
|
||||||
private final Log _log;
|
private final Log _log;
|
||||||
|
private final ByteCache _cache = ByteCache.getInstance(32, 4*1024);
|
||||||
private final ConnectionManager _manager;
|
private final ConnectionManager _manager;
|
||||||
private final LinkedBlockingQueue<Packet> _synQueue;
|
private final LinkedBlockingQueue<Packet> _synQueue;
|
||||||
private final SimpleTimer2 _timer;
|
private final SimpleTimer2 _timer;
|
||||||
@ -259,21 +262,29 @@ class ConnectionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a reset in response to this packet, but only if it
|
||||||
|
* contains a FROM field and Signature that can be verified.
|
||||||
|
*
|
||||||
|
* @param packet the incoming packet we're responding to
|
||||||
|
*/
|
||||||
private void sendReset(Packet packet) {
|
private void sendReset(Packet packet) {
|
||||||
boolean ok = packet.verifySignature(_context, packet.getOptionalFrom(), null);
|
ByteArray ba = _cache.acquire();
|
||||||
|
boolean ok = packet.verifySignature(_context, ba.getData());
|
||||||
|
_cache.release(ba);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldWarn())
|
||||||
_log.warn("Received a spoofed SYN packet: they said they were " + packet.getOptionalFrom());
|
_log.warn("Can't send reset in response to packet: " + packet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PacketLocal reply = new PacketLocal(_context, packet.getOptionalFrom(), packet.getSession());
|
PacketLocal reply = new PacketLocal(_context, packet.getOptionalFrom(), packet.getSession());
|
||||||
reply.setFlag(Packet.FLAG_RESET);
|
reply.setFlag(Packet.FLAG_RESET | Packet.FLAG_SIGNATURE_INCLUDED);
|
||||||
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
|
|
||||||
reply.setAckThrough(packet.getSequenceNum());
|
reply.setAckThrough(packet.getSequenceNum());
|
||||||
reply.setSendStreamId(packet.getReceiveStreamId());
|
reply.setSendStreamId(packet.getReceiveStreamId());
|
||||||
reply.setReceiveStreamId(0);
|
reply.setReceiveStreamId(0);
|
||||||
// TODO remove this someday, as of 0.9.20 we do not require it
|
// As of 0.9.20 we do not require FROM
|
||||||
reply.setOptionalFrom();
|
// Removed in 0.9.39
|
||||||
|
//reply.setOptionalFrom();
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Sending RST: " + reply + " because of " + packet);
|
_log.debug("Sending RST: " + reply + " because of " + packet);
|
||||||
// this just sends the packet - no retries or whatnot
|
// this just sends the packet - no retries or whatnot
|
||||||
|
@ -4,8 +4,11 @@ import java.util.List;
|
|||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.I2PException;
|
import net.i2p.I2PException;
|
||||||
|
import net.i2p.data.ByteArray;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
|
import net.i2p.util.ByteCache;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
import net.i2p.util.SimpleTimer;
|
import net.i2p.util.SimpleTimer;
|
||||||
|
|
||||||
@ -23,6 +26,7 @@ import net.i2p.util.SimpleTimer;
|
|||||||
class ConnectionPacketHandler {
|
class ConnectionPacketHandler {
|
||||||
private final I2PAppContext _context;
|
private final I2PAppContext _context;
|
||||||
private final Log _log;
|
private final Log _log;
|
||||||
|
private final ByteCache _cache = ByteCache.getInstance(32, 4*1024);
|
||||||
|
|
||||||
public static final int MAX_SLOW_START_WINDOW = 24;
|
public static final int MAX_SLOW_START_WINDOW = 24;
|
||||||
|
|
||||||
@ -514,6 +518,7 @@ class ConnectionPacketHandler {
|
|||||||
|
|
||||||
if (con.getSendStreamId() <= 0) {
|
if (con.getSendStreamId() <= 0) {
|
||||||
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
|
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
|
||||||
|
// this is an incoming connection.
|
||||||
con.setSendStreamId(packet.getReceiveStreamId());
|
con.setSendStreamId(packet.getReceiveStreamId());
|
||||||
Destination dest = packet.getOptionalFrom();
|
Destination dest = packet.getOptionalFrom();
|
||||||
if (dest == null) {
|
if (dest == null) {
|
||||||
@ -522,6 +527,9 @@ class ConnectionPacketHandler {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
con.setRemotePeer(dest);
|
con.setRemotePeer(dest);
|
||||||
|
SigningPublicKey spk = packet.getTransientSPK();
|
||||||
|
if (spk != null)
|
||||||
|
con.setRemoteTransientSPK(spk);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// neither RST nor SYN and we dont have the stream id yet?
|
// neither RST nor SYN and we dont have the stream id yet?
|
||||||
@ -535,6 +543,7 @@ class ConnectionPacketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// getting a lot of these - why? mostly/all for acks...
|
||||||
if (con.getSendStreamId() != packet.getReceiveStreamId()) {
|
if (con.getSendStreamId() != packet.getReceiveStreamId()) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Packet received with the wrong reply stream id: "
|
_log.warn("Packet received with the wrong reply stream id: "
|
||||||
@ -553,20 +562,22 @@ class ConnectionPacketHandler {
|
|||||||
* Prior to 0.9.20, the reset packet must contain a FROM field,
|
* Prior to 0.9.20, the reset packet must contain a FROM field,
|
||||||
* and we used that for verification.
|
* and we used that for verification.
|
||||||
* As of 0.9.20, we correctly use the connection's remote peer.
|
* As of 0.9.20, we correctly use the connection's remote peer.
|
||||||
|
*
|
||||||
|
* @param con non-null
|
||||||
*/
|
*/
|
||||||
private void verifyReset(Packet packet, Connection con) {
|
private void verifyReset(Packet packet, Connection con) {
|
||||||
if (con.getReceiveStreamId() == packet.getSendStreamId()) {
|
if (con.getReceiveStreamId() == packet.getSendStreamId()) {
|
||||||
Destination from = con.getRemotePeer();
|
SigningPublicKey spk = con.getRemoteSPK();
|
||||||
if (from == null)
|
ByteArray ba = _cache.acquire();
|
||||||
from = packet.getOptionalFrom();
|
boolean ok = packet.verifySignature(_context, spk, ba.getData());
|
||||||
boolean ok = packet.verifySignature(_context, from, null);
|
_cache.release(ba);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.error("Received unsigned / forged RST on " + con);
|
_log.error("Received unsigned / forged RST on " + con);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldWarn())
|
||||||
_log.debug("Reset received");
|
_log.warn("Reset received on " + con);;
|
||||||
// ok, valid RST
|
// ok, valid RST
|
||||||
con.resetReceived();
|
con.resetReceived();
|
||||||
con.eventOccurred();
|
con.eventOccurred();
|
||||||
@ -587,17 +598,18 @@ class ConnectionPacketHandler {
|
|||||||
/**
|
/**
|
||||||
* Verify the signature if necessary.
|
* Verify the signature if necessary.
|
||||||
*
|
*
|
||||||
|
* @param con non-null
|
||||||
* @throws I2PException if the signature was necessary and it was invalid
|
* @throws I2PException if the signature was necessary and it was invalid
|
||||||
*/
|
*/
|
||||||
private void verifySignature(Packet packet, Connection con) throws I2PException {
|
private void verifySignature(Packet packet, Connection con) throws I2PException {
|
||||||
// verify the signature if necessary
|
// verify the signature if necessary
|
||||||
if (con.getOptions().getRequireFullySigned() ||
|
if (con.getOptions().getRequireFullySigned() ||
|
||||||
packet.isFlagSet(Packet.FLAG_SYNCHRONIZE | Packet.FLAG_CLOSE)) {
|
packet.isFlagSet(Packet.FLAG_SYNCHRONIZE | Packet.FLAG_CLOSE | Packet.FLAG_SIGNATURE_INCLUDED)) {
|
||||||
// we need a valid signature
|
// we need a valid signature
|
||||||
Destination from = con.getRemotePeer();
|
SigningPublicKey spk = con.getRemoteSPK();
|
||||||
if (from == null)
|
ByteArray ba = _cache.acquire();
|
||||||
from = packet.getOptionalFrom();
|
boolean sigOk = packet.verifySignature(_context, spk, null);
|
||||||
boolean sigOk = packet.verifySignature(_context, from, null);
|
_cache.release(ba);
|
||||||
if (!sigOk) {
|
if (!sigOk) {
|
||||||
throw new I2PException("Received unsigned / forged packet: " + packet);
|
throw new I2PException("Received unsigned / forged packet: " + packet);
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,10 @@ class MessageHandler implements I2PSessionMuxedListener {
|
|||||||
packet.setRemotePort(fromPort);
|
packet.setRemotePort(fromPort);
|
||||||
packet.setLocalPort(toPort);
|
packet.setLocalPort(toPort);
|
||||||
_manager.getPacketHandler().receivePacket(packet);
|
_manager.getPacketHandler().receivePacket(packet);
|
||||||
|
} catch (IndexOutOfBoundsException ioobe) {
|
||||||
|
_context.statManager().addRateData("stream.packetReceiveFailure", 1);
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Received an invalid packet", ioobe);
|
||||||
} catch (IllegalArgumentException iae) {
|
} catch (IllegalArgumentException iae) {
|
||||||
_context.statManager().addRateData("stream.packetReceiveFailure", 1);
|
_context.statManager().addRateData("stream.packetReceiveFailure", 1);
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package net.i2p.client.streaming.impl;
|
package net.i2p.client.streaming.impl;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.client.I2PSession;
|
import net.i2p.client.I2PSession;
|
||||||
@ -82,6 +84,11 @@ class Packet {
|
|||||||
protected Destination _optionFrom;
|
protected Destination _optionFrom;
|
||||||
private int _optionDelay;
|
private int _optionDelay;
|
||||||
private int _optionMaxSize;
|
private int _optionMaxSize;
|
||||||
|
// following 3 for ofline sigs
|
||||||
|
protected long _transientExpires;
|
||||||
|
protected Signature _offlineSignature;
|
||||||
|
protected SigningPublicKey _transientSigningPublicKey;
|
||||||
|
// ports
|
||||||
private int _localPort;
|
private int _localPort;
|
||||||
private int _remotePort;
|
private int _remotePort;
|
||||||
|
|
||||||
@ -161,6 +168,12 @@ class Packet {
|
|||||||
* If set, this packet doesn't really want to ack anything
|
* If set, this packet doesn't really want to ack anything
|
||||||
*/
|
*/
|
||||||
public static final int FLAG_NO_ACK = (1 << 10);
|
public static final int FLAG_NO_ACK = (1 << 10);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, an offline signing block is in the options.
|
||||||
|
* @since 0.9.39
|
||||||
|
*/
|
||||||
|
public static final int FLAG_SIGNATURE_OFFLINE = (1 << 11);
|
||||||
|
|
||||||
public static final int DEFAULT_MAX_SIZE = 32*1024;
|
public static final int DEFAULT_MAX_SIZE = 32*1024;
|
||||||
protected static final int MAX_DELAY_REQUEST = 65535;
|
protected static final int MAX_DELAY_REQUEST = 65535;
|
||||||
@ -348,15 +361,12 @@ class Packet {
|
|||||||
*/
|
*/
|
||||||
public Destination getOptionalFrom() { return _optionFrom; }
|
public Destination getOptionalFrom() { return _optionFrom; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This sets the from field in the packet to the Destination for the session
|
* Only if an offline signing block was included, else null
|
||||||
* provided in the constructor.
|
*
|
||||||
* This also sets flag FLAG_FROM_INCLUDED
|
* @since 0.9.39
|
||||||
*/
|
*/
|
||||||
public void setOptionalFrom() {
|
public SigningPublicKey getTransientSPK() { return _transientSigningPublicKey; }
|
||||||
setFlag(FLAG_FROM_INCLUDED, true);
|
|
||||||
_optionFrom = _session.getMyDestination();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How many milliseconds the sender of this packet wants the recipient
|
* How many milliseconds the sender of this packet wants the recipient
|
||||||
@ -478,6 +488,11 @@ class Packet {
|
|||||||
optionSize += _optionFrom.size();
|
optionSize += _optionFrom.size();
|
||||||
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED))
|
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED))
|
||||||
optionSize += 2;
|
optionSize += 2;
|
||||||
|
if (isFlagSet(FLAG_SIGNATURE_OFFLINE)) {
|
||||||
|
optionSize += 6;
|
||||||
|
optionSize += _transientSigningPublicKey.length();
|
||||||
|
optionSize += _offlineSignature.length();
|
||||||
|
}
|
||||||
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
||||||
if (fakeSigLen > 0)
|
if (fakeSigLen > 0)
|
||||||
optionSize += fakeSigLen;
|
optionSize += fakeSigLen;
|
||||||
@ -501,6 +516,18 @@ class Packet {
|
|||||||
DataHelper.toLong(buffer, cur, 2, _optionMaxSize > 0 ? _optionMaxSize : DEFAULT_MAX_SIZE);
|
DataHelper.toLong(buffer, cur, 2, _optionMaxSize > 0 ? _optionMaxSize : DEFAULT_MAX_SIZE);
|
||||||
cur += 2;
|
cur += 2;
|
||||||
}
|
}
|
||||||
|
if (isFlagSet(FLAG_SIGNATURE_OFFLINE)) {
|
||||||
|
DataHelper.toLong(buffer, cur, 4, _transientExpires / 1000);
|
||||||
|
cur += 4;
|
||||||
|
DataHelper.toLong(buffer, cur, 2, _transientSigningPublicKey.getType().getCode());
|
||||||
|
cur += 2;
|
||||||
|
int len = _transientSigningPublicKey.length();
|
||||||
|
System.arraycopy(_transientSigningPublicKey.getData(), 0, buffer, cur, len);
|
||||||
|
cur += len;
|
||||||
|
len = _offlineSignature.length();
|
||||||
|
System.arraycopy(_offlineSignature.getData(), 0, buffer, cur, len);
|
||||||
|
cur += len;
|
||||||
|
}
|
||||||
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
||||||
if (fakeSigLen == 0) {
|
if (fakeSigLen == 0) {
|
||||||
// we're signing (or validating)
|
// we're signing (or validating)
|
||||||
@ -535,9 +562,8 @@ class Packet {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* how large would this packet be if we wrote it
|
* how large would this packet be if we wrote it
|
||||||
* @return How large the current packet would be
|
|
||||||
*
|
*
|
||||||
* @throws IllegalStateException
|
* @return How large the current packet would be
|
||||||
*/
|
*/
|
||||||
private int writtenSize() {
|
private int writtenSize() {
|
||||||
//int size = 0;
|
//int size = 0;
|
||||||
@ -564,6 +590,11 @@ class Packet {
|
|||||||
size += 2;
|
size += 2;
|
||||||
if (isFlagSet(FLAG_SIGNATURE_INCLUDED))
|
if (isFlagSet(FLAG_SIGNATURE_INCLUDED))
|
||||||
size += _optionSignature.length();
|
size += _optionSignature.length();
|
||||||
|
if (isFlagSet(FLAG_SIGNATURE_OFFLINE)) {
|
||||||
|
size += 6;
|
||||||
|
size += _transientSigningPublicKey.length();
|
||||||
|
size += _offlineSignature.length();
|
||||||
|
}
|
||||||
|
|
||||||
if (_payload != null) {
|
if (_payload != null) {
|
||||||
size += _payload.getValid();
|
size += _payload.getValid();
|
||||||
@ -583,6 +614,7 @@ class Packet {
|
|||||||
* part of the packet?
|
* part of the packet?
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException if the data is b0rked
|
* @throws IllegalArgumentException if the data is b0rked
|
||||||
|
* @throws IndexOutOfBoundsException if the data is b0rked
|
||||||
*/
|
*/
|
||||||
public void readPacket(byte buffer[], int offset, int length) throws IllegalArgumentException {
|
public void readPacket(byte buffer[], int offset, int length) throws IllegalArgumentException {
|
||||||
if (buffer.length - offset < length)
|
if (buffer.length - offset < length)
|
||||||
@ -657,11 +689,39 @@ class Packet {
|
|||||||
setOptionalMaxSize((int)DataHelper.fromLong(buffer, cur, 2));
|
setOptionalMaxSize((int)DataHelper.fromLong(buffer, cur, 2));
|
||||||
cur += 2;
|
cur += 2;
|
||||||
}
|
}
|
||||||
|
if (isFlagSet(FLAG_SIGNATURE_OFFLINE)) {
|
||||||
|
_transientExpires = DataHelper.fromLong(buffer, cur, 4) * 1000;
|
||||||
|
cur += 4;
|
||||||
|
int itype = (int) DataHelper.fromLong(buffer, cur, 2);
|
||||||
|
cur += 2;
|
||||||
|
SigType type = SigType.getByCode(itype);
|
||||||
|
if (type == null || !type.isAvailable())
|
||||||
|
throw new IllegalArgumentException("Unsupported transient sig type: " + itype);
|
||||||
|
_transientSigningPublicKey = new SigningPublicKey(type);
|
||||||
|
byte[] buf = new byte[_transientSigningPublicKey.length()];
|
||||||
|
System.arraycopy(buffer, cur, buf, 0, buf.length);
|
||||||
|
_transientSigningPublicKey.setData(buf);
|
||||||
|
cur += buf.length;
|
||||||
|
if (_optionFrom != null) {
|
||||||
|
type = _optionFrom.getSigningPublicKey().getType();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("TODO offline w/o FROM");
|
||||||
|
}
|
||||||
|
_offlineSignature = new Signature(type);
|
||||||
|
buf = new byte[_offlineSignature.length()];
|
||||||
|
System.arraycopy(buffer, cur, buf, 0, buf.length);
|
||||||
|
_offlineSignature.setData(buf);
|
||||||
|
cur += buf.length;
|
||||||
|
}
|
||||||
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
||||||
Signature optionSignature;
|
Signature optionSignature;
|
||||||
Destination from = getOptionalFrom();
|
if (_optionFrom != null) {
|
||||||
if (from != null) {
|
SigType type;
|
||||||
optionSignature = new Signature(from.getSigningPublicKey().getType());
|
if (isFlagSet(FLAG_SIGNATURE_OFFLINE))
|
||||||
|
type = _transientSigningPublicKey.getType();
|
||||||
|
else
|
||||||
|
type = _optionFrom.getSigningPublicKey().getType();
|
||||||
|
optionSignature = new Signature(type);
|
||||||
} else {
|
} else {
|
||||||
// super cheat for now, look for correct type,
|
// super cheat for now, look for correct type,
|
||||||
// assume no more options. If we add to the options
|
// assume no more options. If we add to the options
|
||||||
@ -694,32 +754,77 @@ class Packet {
|
|||||||
cur += buf.length;
|
cur += buf.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the signature on the data is valid.
|
||||||
|
* Packet MUST have a FROM option or will return false.
|
||||||
|
*
|
||||||
|
* @param ctx Application context
|
||||||
|
* @param buffer data to validate with signature, or null to use our own buffer.
|
||||||
|
* @return true if the signature exists and validates against the data,
|
||||||
|
* false otherwise.
|
||||||
|
* @since 0.9.39
|
||||||
|
*/
|
||||||
|
public boolean verifySignature(I2PAppContext ctx, byte buffer[]) {
|
||||||
|
return verifySignature(ctx, null, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the signature on the data is valid.
|
* Determine whether the signature on the data is valid.
|
||||||
*
|
*
|
||||||
* @param ctx Application context
|
* @param ctx Application context
|
||||||
* @param from the Destination the data came from
|
* @param spk Signing key to verify with, ONLY if there is no FROM field in this packet.
|
||||||
* @param buffer data to validate with signature
|
* May be the SPK from a FROM field or offline sig field from a previous packet on this connection.
|
||||||
|
* Ignored if this packet contains a FROM option block.
|
||||||
|
* Null ok if none available.
|
||||||
|
* @param buffer data to validate with signature, or null to use our own buffer.
|
||||||
* @return true if the signature exists and validates against the data,
|
* @return true if the signature exists and validates against the data,
|
||||||
* false otherwise.
|
* false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean verifySignature(I2PAppContext ctx, Destination from, byte buffer[]) {
|
public boolean verifySignature(I2PAppContext ctx, SigningPublicKey altSPK, byte buffer[]) {
|
||||||
if (!isFlagSet(FLAG_SIGNATURE_INCLUDED)) return false;
|
if (!isFlagSet(FLAG_SIGNATURE_INCLUDED)) return false;
|
||||||
if (_optionSignature == null) return false;
|
if (_optionSignature == null) return false;
|
||||||
|
SigningPublicKey spk = _optionFrom != null ? _optionFrom.getSigningPublicKey() : altSPK;
|
||||||
// prevent receiveNewSyn() ... !active ... sendReset() ... verifySignature ... NPE
|
// prevent receiveNewSyn() ... !active ... sendReset() ... verifySignature ... NPE
|
||||||
if (from == null) return false;
|
if (spk == null) return false;
|
||||||
|
|
||||||
int size = writtenSize();
|
int size = writtenSize();
|
||||||
|
|
||||||
if (buffer == null)
|
if (buffer == null)
|
||||||
buffer = new byte[size];
|
buffer = new byte[size];
|
||||||
SigningPublicKey spk = from.getSigningPublicKey();
|
if (isFlagSet(FLAG_SIGNATURE_OFFLINE)) {
|
||||||
|
if (_transientExpires < ctx.clock().now()) {
|
||||||
|
Log l = ctx.logManager().getLog(Packet.class);
|
||||||
|
if (l.shouldLog(Log.WARN))
|
||||||
|
l.warn("Offline signature expired " + toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(6 + _transientSigningPublicKey.length());
|
||||||
|
try {
|
||||||
|
DataHelper.writeLong(baos, 4, _transientExpires / 1000);
|
||||||
|
DataHelper.writeLong(baos, 2, _transientSigningPublicKey.getType().getCode());
|
||||||
|
_transientSigningPublicKey.writeBytes(baos);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
return false;
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
byte[] data = baos.toByteArray();
|
||||||
|
boolean ok = ctx.dsa().verifySignature(_offlineSignature, data, 0, data.length, spk);
|
||||||
|
if (!ok) {
|
||||||
|
Log l = ctx.logManager().getLog(Packet.class);
|
||||||
|
if (l.shouldLog(Log.WARN))
|
||||||
|
l.warn("Offline signature failed on " + toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// use transient key to verify
|
||||||
|
spk = _transientSigningPublicKey;
|
||||||
|
}
|
||||||
SigType type = spk.getType();
|
SigType type = spk.getType();
|
||||||
if (type == null) {
|
if (type == null || !type.isAvailable()) {
|
||||||
Log l = ctx.logManager().getLog(Packet.class);
|
Log l = ctx.logManager().getLog(Packet.class);
|
||||||
if (l.shouldLog(Log.WARN))
|
if (l.shouldLog(Log.WARN))
|
||||||
l.warn("Unknown sig type in " + from + " cannot verify " + toString());
|
l.warn("Unknown sig type in " + spk + " cannot verify " + toString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int written = writePacket(buffer, 0, type.getSigLen());
|
int written = writePacket(buffer, 0, type.getSigLen());
|
||||||
@ -731,18 +836,27 @@ class Packet {
|
|||||||
// Fixup of signature if we guessed wrong on the type in readPacket(), which could happen
|
// Fixup of signature if we guessed wrong on the type in readPacket(), which could happen
|
||||||
// on a close or reset packet where we have a signature without a FROM
|
// on a close or reset packet where we have a signature without a FROM
|
||||||
if (type != _optionSignature.getType() &&
|
if (type != _optionSignature.getType() &&
|
||||||
type.getSigLen() == _optionSignature.length())
|
type.getSigLen() == _optionSignature.length()) {
|
||||||
|
//Log l = ctx.logManager().getLog(Packet.class);
|
||||||
|
//if (l.shouldDebug())
|
||||||
|
// l.debug("Fixing up sig type from " + _optionSignature.getType() + " to " + type);
|
||||||
_optionSignature = new Signature(type, _optionSignature.getData());
|
_optionSignature = new Signature(type, _optionSignature.getData());
|
||||||
|
}
|
||||||
|
|
||||||
boolean ok = ctx.dsa().verifySignature(_optionSignature, buffer, 0, size, spk);
|
boolean ok;
|
||||||
|
try {
|
||||||
|
ok = ctx.dsa().verifySignature(_optionSignature, buffer, 0, size, spk);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
// sigtype mismatch
|
||||||
|
Log l = ctx.logManager().getLog(Packet.class);
|
||||||
|
if (l.shouldLog(Log.WARN))
|
||||||
|
l.warn("Signature failed on " + toString(), iae);
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
Log l = ctx.logManager().getLog(Packet.class);
|
Log l = ctx.logManager().getLog(Packet.class);
|
||||||
if (l.shouldLog(Log.WARN))
|
if (l.shouldLog(Log.WARN))
|
||||||
l.warn("Signature failed on " + toString(), new Exception("moo"));
|
l.warn("Signature failed on " + toString() + " using SPK " + spk);
|
||||||
//if (false) {
|
|
||||||
// l.error(Base64.encode(buffer, 0, size));
|
|
||||||
// l.error("Signature: " + Base64.encode(_optionSignature.getData()));
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
@ -776,6 +890,11 @@ class Packet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final void toFlagString(StringBuilder buf) {
|
private final void toFlagString(StringBuilder buf) {
|
||||||
|
if (isFlagSet(FLAG_SYNCHRONIZE)) buf.append(" SYN");
|
||||||
|
if (isFlagSet(FLAG_CLOSE)) buf.append(" CLOSE");
|
||||||
|
if (isFlagSet(FLAG_RESET)) buf.append(" RESET");
|
||||||
|
if (isFlagSet(FLAG_ECHO)) buf.append(" ECHO");
|
||||||
|
if (isFlagSet(FLAG_FROM_INCLUDED)) buf.append(" FROM ").append(_optionFrom.size());
|
||||||
if (isFlagSet(FLAG_NO_ACK))
|
if (isFlagSet(FLAG_NO_ACK))
|
||||||
buf.append(" NO_ACK");
|
buf.append(" NO_ACK");
|
||||||
else
|
else
|
||||||
@ -786,21 +905,30 @@ class Packet {
|
|||||||
buf.append(' ').append(_nacks[i]);
|
buf.append(' ').append(_nacks[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isFlagSet(FLAG_CLOSE)) buf.append(" CLOSE");
|
|
||||||
if (isFlagSet(FLAG_DELAY_REQUESTED)) buf.append(" DELAY ").append(_optionDelay);
|
if (isFlagSet(FLAG_DELAY_REQUESTED)) buf.append(" DELAY ").append(_optionDelay);
|
||||||
if (isFlagSet(FLAG_ECHO)) buf.append(" ECHO");
|
|
||||||
if (isFlagSet(FLAG_FROM_INCLUDED)) buf.append(" FROM ").append(_optionFrom.size());
|
|
||||||
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED)) buf.append(" MS ").append(_optionMaxSize);
|
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED)) buf.append(" MS ").append(_optionMaxSize);
|
||||||
if (isFlagSet(FLAG_PROFILE_INTERACTIVE)) buf.append(" INTERACTIVE");
|
if (isFlagSet(FLAG_PROFILE_INTERACTIVE)) buf.append(" INTERACTIVE");
|
||||||
if (isFlagSet(FLAG_RESET)) buf.append(" RESET");
|
if (isFlagSet(FLAG_SIGNATURE_REQUESTED)) buf.append(" SIGREQ");
|
||||||
|
if (isFlagSet(FLAG_SIGNATURE_OFFLINE)) {
|
||||||
|
if (_transientExpires != 0)
|
||||||
|
buf.append(" TRANSEXP ").append(new Date(_transientExpires));
|
||||||
|
else
|
||||||
|
buf.append(" (no expiration)");
|
||||||
|
if (_transientSigningPublicKey != null)
|
||||||
|
buf.append(" TRANSKEY ").append(_transientSigningPublicKey.getType());
|
||||||
|
else
|
||||||
|
buf.append(" (no key data)");
|
||||||
|
if (_offlineSignature != null)
|
||||||
|
buf.append(" OFFSIG ").append(_offlineSignature.getType());
|
||||||
|
else
|
||||||
|
buf.append(" (no offline sig data)");
|
||||||
|
}
|
||||||
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
||||||
if (_optionSignature != null)
|
if (_optionSignature != null)
|
||||||
buf.append(" SIG ").append(_optionSignature.length());
|
buf.append(" SIG ").append(_optionSignature.getType());
|
||||||
else
|
else
|
||||||
buf.append(" (to be signed)");
|
buf.append(" (to be signed)");
|
||||||
}
|
}
|
||||||
if (isFlagSet(FLAG_SIGNATURE_REQUESTED)) buf.append(" SIGREQ");
|
|
||||||
if (isFlagSet(FLAG_SYNCHRONIZE)) buf.append(" SYN");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generate a pcap/tcpdump-compatible format,
|
/** Generate a pcap/tcpdump-compatible format,
|
||||||
|
@ -5,7 +5,10 @@ import java.util.Date;
|
|||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.I2PException;
|
import net.i2p.I2PException;
|
||||||
|
import net.i2p.data.ByteArray;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
|
import net.i2p.util.ByteCache;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,6 +21,7 @@ class PacketHandler {
|
|||||||
private final ConnectionManager _manager;
|
private final ConnectionManager _manager;
|
||||||
private final I2PAppContext _context;
|
private final I2PAppContext _context;
|
||||||
private final Log _log;
|
private final Log _log;
|
||||||
|
private final ByteCache _cache = ByteCache.getInstance(32, 4*1024);
|
||||||
//private int _lastDelay;
|
//private int _lastDelay;
|
||||||
//private int _dropped;
|
//private int _dropped;
|
||||||
|
|
||||||
@ -179,8 +183,11 @@ class PacketHandler {
|
|||||||
long oldId = con.getSendStreamId();
|
long oldId = con.getSendStreamId();
|
||||||
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
|
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
|
||||||
if (oldId <= 0) {
|
if (oldId <= 0) {
|
||||||
// con fully established, w00t
|
// outgoing con now fully established
|
||||||
con.setSendStreamId(packet.getReceiveStreamId());
|
con.setSendStreamId(packet.getReceiveStreamId());
|
||||||
|
SigningPublicKey spk = packet.getTransientSPK();
|
||||||
|
if (spk != null)
|
||||||
|
con.setRemoteTransientSPK(spk);
|
||||||
} else if (oldId == packet.getReceiveStreamId()) {
|
} else if (oldId == packet.getReceiveStreamId()) {
|
||||||
// ok, as expected...
|
// ok, as expected...
|
||||||
} else {
|
} else {
|
||||||
@ -195,9 +202,14 @@ class PacketHandler {
|
|||||||
try {
|
try {
|
||||||
con.getPacketHandler().receivePacket(packet, con);
|
con.getPacketHandler().receivePacket(packet, con);
|
||||||
} catch (I2PException ie) {
|
} catch (I2PException ie) {
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldWarn())
|
||||||
_log.error("Received forged packet for " + con + "/" + oldId + ": " + packet, ie);
|
_log.warn("Sig verify fail for " + con + "/" + oldId + ": " + packet, ie);
|
||||||
con.setSendStreamId(oldId);
|
con.setSendStreamId(oldId);
|
||||||
|
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
|
||||||
|
// send a reset, it's a known con, so it's unlikely to be spoofed
|
||||||
|
// don't bother to send reset if it's just a CLOSE
|
||||||
|
sendResetUnverified(packet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
|
} else if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
@ -238,19 +250,34 @@ class PacketHandler {
|
|||||||
Destination from = packet.getOptionalFrom();
|
Destination from = packet.getOptionalFrom();
|
||||||
if (from == null)
|
if (from == null)
|
||||||
return;
|
return;
|
||||||
boolean ok = packet.verifySignature(_context, from, null);
|
ByteArray ba = _cache.acquire();
|
||||||
|
boolean ok = packet.verifySignature(_context, ba.getData());
|
||||||
|
_cache.release(ba);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Can't send reset after recv spoofed packet: " + packet);
|
_log.warn("Can't send reset in response to packet: " + packet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PacketLocal reply = new PacketLocal(_context, from, packet.getSession());
|
sendResetUnverified(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sends a reset back to the place this packet came from.
|
||||||
|
* Packet MUST have a FROM option.
|
||||||
|
* This is not associated with a connection, so no con stats are updated.
|
||||||
|
*
|
||||||
|
* @param packet incoming packet to be replied to, MUST have a FROM option
|
||||||
|
* @since 0.9.39
|
||||||
|
*/
|
||||||
|
private void sendResetUnverified(Packet packet) {
|
||||||
|
PacketLocal reply = new PacketLocal(_context, packet.getOptionalFrom(), packet.getSession());
|
||||||
reply.setFlag(Packet.FLAG_RESET);
|
reply.setFlag(Packet.FLAG_RESET);
|
||||||
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
|
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
|
||||||
reply.setSendStreamId(packet.getReceiveStreamId());
|
reply.setSendStreamId(packet.getReceiveStreamId());
|
||||||
reply.setReceiveStreamId(packet.getSendStreamId());
|
reply.setReceiveStreamId(packet.getSendStreamId());
|
||||||
// TODO remove this someday, as of 0.9.20 we do not require it
|
// As of 0.9.20 we do not require FROM
|
||||||
reply.setOptionalFrom();
|
// Removed in 0.9.39
|
||||||
|
//reply.setOptionalFrom();
|
||||||
reply.setLocalPort(packet.getLocalPort());
|
reply.setLocalPort(packet.getLocalPort());
|
||||||
reply.setRemotePort(packet.getRemotePort());
|
reply.setRemotePort(packet.getRemotePort());
|
||||||
// this just sends the packet - no retries or whatnot
|
// this just sends the packet - no retries or whatnot
|
||||||
@ -348,17 +375,13 @@ class PacketHandler {
|
|||||||
* @param con null if unknown
|
* @param con null if unknown
|
||||||
*/
|
*/
|
||||||
private void receivePing(Connection con, Packet packet) {
|
private void receivePing(Connection con, Packet packet) {
|
||||||
boolean ok = packet.verifySignature(_context, packet.getOptionalFrom(), null);
|
SigningPublicKey spk = con != null ? con.getRemoteSPK() : null;
|
||||||
|
ByteArray ba = _cache.acquire();
|
||||||
|
boolean ok = packet.verifySignature(_context, spk, ba.getData());
|
||||||
|
_cache.release(ba);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
if (_log.shouldLog(Log.WARN)) {
|
if (_log.shouldLog(Log.WARN))
|
||||||
if (packet.getOptionalFrom() == null)
|
_log.warn("Bad ping, dropping: " + packet);
|
||||||
_log.warn("Ping with no from (flagged? " + packet.isFlagSet(Packet.FLAG_FROM_INCLUDED) + ")");
|
|
||||||
else if (packet.getOptionalSignature() == null)
|
|
||||||
_log.warn("Ping with no signature (flagged? " + packet.isFlagSet(Packet.FLAG_SIGNATURE_INCLUDED) + ")");
|
|
||||||
else
|
|
||||||
_log.warn("Forged ping, discard (from=" + packet.getOptionalFrom().calculateHash().toBase64()
|
|
||||||
+ " sig=" + packet.getOptionalSignature().toBase64() + ")");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
_manager.receivePing(con, packet);
|
_manager.receivePing(con, packet);
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,25 @@ class PacketLocal extends Packet implements MessageOutputStream.WriteStatus {
|
|||||||
_lastSend = -1;
|
_lastSend = -1;
|
||||||
_cancelledOn = -1;
|
_cancelledOn = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sets the from field in the packet to the Destination for the session
|
||||||
|
* provided in the constructor.
|
||||||
|
* This also sets flag FLAG_FROM_INCLUDED.
|
||||||
|
* Also, as of 0.9.39, sets the offline signing data if supported by the session.
|
||||||
|
*
|
||||||
|
* @since 0.9.39 moved from super
|
||||||
|
*/
|
||||||
|
public void setOptionalFrom() {
|
||||||
|
setFlag(FLAG_FROM_INCLUDED, true);
|
||||||
|
_optionFrom = _session.getMyDestination();
|
||||||
|
if (_session.isOffline()) {
|
||||||
|
setFlag(FLAG_SIGNATURE_OFFLINE, true);
|
||||||
|
_transientExpires = _session.getOfflineExpiration();
|
||||||
|
_transientSigningPublicKey = _session.getTransientSigningPublicKey();
|
||||||
|
_offlineSignature = _session.getOfflineSignature();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Destination getTo() { return _to; }
|
public Destination getTo() { return _to; }
|
||||||
|
|
||||||
@ -227,11 +246,21 @@ class PacketLocal extends Packet implements MessageOutputStream.WriteStatus {
|
|||||||
//+ 1 // resendDelay
|
//+ 1 // resendDelay
|
||||||
//+ 2 // flags
|
//+ 2 // flags
|
||||||
//+ 2 // optionSize
|
//+ 2 // optionSize
|
||||||
+ 21
|
//+ 1 // nacksSize
|
||||||
+ (_nacks != null ? 4*_nacks.length + 1 : 1)
|
+ 22;
|
||||||
+ (isFlagSet(FLAG_DELAY_REQUESTED) ? 2 : 0)
|
if (_nacks != null)
|
||||||
+ (isFlagSet(FLAG_FROM_INCLUDED) ? _optionFrom.size() : 0)
|
signatureOffset += 4 *_nacks.length;
|
||||||
+ (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED) ? 2 : 0);
|
if (isFlagSet(FLAG_DELAY_REQUESTED))
|
||||||
|
signatureOffset += 2;
|
||||||
|
if (isFlagSet(FLAG_FROM_INCLUDED))
|
||||||
|
signatureOffset += _optionFrom.size();
|
||||||
|
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED))
|
||||||
|
signatureOffset += 2;
|
||||||
|
if (isFlagSet(FLAG_SIGNATURE_OFFLINE)) {
|
||||||
|
signatureOffset += 6;
|
||||||
|
signatureOffset += _transientSigningPublicKey.length();
|
||||||
|
signatureOffset += _offlineSignature.length();
|
||||||
|
}
|
||||||
System.arraycopy(_optionSignature.getData(), 0, buffer, signatureOffset, _optionSignature.length());
|
System.arraycopy(_optionSignature.getData(), 0, buffer, signatureOffset, _optionSignature.length());
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
15
history.txt
15
history.txt
@ -1,3 +1,18 @@
|
|||||||
|
2019-02-03 zzz
|
||||||
|
* I2CP:
|
||||||
|
- Remove revocation private key from CreateLeaseset2 message
|
||||||
|
- Use correct key to sign SessionConfig with offline keys
|
||||||
|
* Streaming:
|
||||||
|
- Support offline signatures (proposal 123)
|
||||||
|
- Don't send FROM in RESET, not required since 0.9.20
|
||||||
|
- Send RESET when SYN signature verification fails
|
||||||
|
- Use cached buffers for signature verification
|
||||||
|
- Always verify packets with signatures, even if not required
|
||||||
|
* Test: Disable NTP in LocalClientManager
|
||||||
|
|
||||||
|
2019-02-02 zzz
|
||||||
|
* Debian: Fix build of i2pcontrol
|
||||||
|
|
||||||
2019-02-01 zzz
|
2019-02-01 zzz
|
||||||
* Debian: AppArmor fix for Oracle JVM (ticket #2319)
|
* Debian: AppArmor fix for Oracle JVM (ticket #2319)
|
||||||
* i2ptunnel:
|
* i2ptunnel:
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 4;
|
public final static long BUILD = 5;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
Reference in New Issue
Block a user