forked from I2P_Developers/i2p.i2p
SSU: Handle RI sig types
TransportManager: Banlist unsupported RI sig types
This commit is contained in:
@ -59,6 +59,7 @@ public class TransportManager implements TransportEventListener {
|
||||
_context = context;
|
||||
_log = _context.logManager().getLog(TransportManager.class);
|
||||
_context.statManager().createRateStat("transport.banlistOnUnreachable", "Add a peer to the banlist since none of the transports can reach them", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("transport.banlistOnUsupportedSigType", "Add a peer to the banlist since signature type is unsupported", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("transport.noBidsYetNotAllUnreachable", "Add a peer to the banlist since none of the transports can reach them", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("transport.bidFailBanlisted", "Could not attempt to bid on message, as they were banlisted", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("transport.bidFailSelf", "Could not attempt to bid on message, as it targeted ourselves", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
@ -499,8 +500,11 @@ public class TransportManager implements TransportEventListener {
|
||||
}
|
||||
}
|
||||
if (unreachableTransports >= _transports.size()) {
|
||||
// Don't banlist if we aren't talking to anybody, as we may have a network connection issue
|
||||
if (unreachableTransports >= _transports.size() && countActivePeers() > 0) {
|
||||
if (msg.getTarget().getIdentity().getSigningPublicKey().getType() == null) {
|
||||
_context.statManager().addRateData("transport.banlistOnUnsupportedSigType", 1);
|
||||
_context.banlist().banlistRouterForever(peer, _x("Unsupported signature type"));
|
||||
} else if (unreachableTransports >= _transports.size() && countActivePeers() > 0) {
|
||||
// Don't banlist if we aren't talking to anybody, as we may have a network connection issue
|
||||
_context.statManager().addRateData("transport.banlistOnUnreachable", msg.getLifetime(), msg.getLifetime());
|
||||
_context.banlist().banlistRouter(peer, _x("Unreachable on any transport"));
|
||||
}
|
||||
|
@ -362,6 +362,12 @@ public class NTCPTransport extends TransportImpl {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check for supported sig type
|
||||
if (toAddress.getIdentity().getSigningPublicKey().getType() == null) {
|
||||
markUnreachable(peer);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!allowConnection()) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("no bid when trying to send to " + peer + ", max connection limit reached");
|
||||
|
@ -5,6 +5,7 @@ import java.io.IOException;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataFormatException;
|
||||
@ -47,6 +48,9 @@ class InboundEstablishState {
|
||||
private long _receivedSignedOnTime;
|
||||
private byte _receivedSignature[];
|
||||
private boolean _verificationAttempted;
|
||||
// sig not verified
|
||||
private RouterIdentity _receivedUnconfirmedIdentity;
|
||||
// identical to uncomfirmed, but sig now verified
|
||||
private RouterIdentity _receivedConfirmedIdentity;
|
||||
// general status
|
||||
private final long _establishBegin;
|
||||
@ -295,9 +299,28 @@ class InboundEstablishState {
|
||||
|
||||
if (cur == _receivedIdentity.length-1) {
|
||||
_receivedSignedOnTime = conf.readFinalFragmentSignedOnTime();
|
||||
if (_receivedSignature == null)
|
||||
_receivedSignature = new byte[Signature.SIGNATURE_BYTES];
|
||||
conf.readFinalSignature(_receivedSignature, 0);
|
||||
// TODO verify time to prevent replay attacks
|
||||
buildIdentity();
|
||||
if (_receivedUnconfirmedIdentity != null) {
|
||||
SigType type = _receivedUnconfirmedIdentity.getSigningPublicKey().getType();
|
||||
if (type != null) {
|
||||
int sigLen = type.getSigLen();
|
||||
if (_receivedSignature == null)
|
||||
_receivedSignature = new byte[sigLen];
|
||||
conf.readFinalSignature(_receivedSignature, 0, sigLen);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unsupported sig type from: " + toString());
|
||||
// _x() in UDPTransport
|
||||
_context.banlist().banlistRouterForever(_receivedUnconfirmedIdentity.calculateHash(),
|
||||
"Unsupported signature type");
|
||||
fail();
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Bad ident from: " + toString());
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
if ( (_currentState == InboundState.IB_STATE_UNKNOWN) ||
|
||||
@ -318,9 +341,10 @@ class InboundEstablishState {
|
||||
*/
|
||||
private boolean confirmedFullyReceived() {
|
||||
if (_receivedIdentity != null) {
|
||||
for (int i = 0; i < _receivedIdentity.length; i++)
|
||||
for (int i = 0; i < _receivedIdentity.length; i++) {
|
||||
if (_receivedIdentity[i] == null)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -339,7 +363,51 @@ class InboundEstablishState {
|
||||
}
|
||||
return _receivedConfirmedIdentity;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct Alice's RouterIdentity.
|
||||
* Must have received all fragments.
|
||||
* Sets _receivedUnconfirmedIdentity, unless invalid.
|
||||
*
|
||||
* Caller must synch on this.
|
||||
*
|
||||
* @since 0.9.16 was in verifyIdentity()
|
||||
*/
|
||||
private void buildIdentity() {
|
||||
if (_receivedUnconfirmedIdentity != null)
|
||||
return; // dup pkt?
|
||||
int frags = _receivedIdentity.length;
|
||||
byte[] ident;
|
||||
if (frags > 1) {
|
||||
int identSize = 0;
|
||||
for (int i = 0; i < _receivedIdentity.length; i++)
|
||||
identSize += _receivedIdentity[i].length;
|
||||
ident = new byte[identSize];
|
||||
int off = 0;
|
||||
for (int i = 0; i < _receivedIdentity.length; i++) {
|
||||
int len = _receivedIdentity[i].length;
|
||||
System.arraycopy(_receivedIdentity[i], 0, ident, off, len);
|
||||
off += len;
|
||||
}
|
||||
} else {
|
||||
// no need to copy
|
||||
ident = _receivedIdentity[0];
|
||||
}
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(ident);
|
||||
RouterIdentity peer = new RouterIdentity();
|
||||
try {
|
||||
peer.readBytes(in);
|
||||
_receivedUnconfirmedIdentity = peer;
|
||||
} catch (DataFormatException dfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Improperly formatted yet fully received ident", dfe);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Improperly formatted yet fully received ident", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if Alice sent us a valid confirmation packet. The
|
||||
* identity signs: Alice's IP + Alice's port + Bob's IP + Bob's port
|
||||
@ -351,21 +419,11 @@ class InboundEstablishState {
|
||||
* Caller must synch on this.
|
||||
*/
|
||||
private void verifyIdentity() {
|
||||
int identSize = 0;
|
||||
for (int i = 0; i < _receivedIdentity.length; i++)
|
||||
identSize += _receivedIdentity[i].length;
|
||||
byte ident[] = new byte[identSize];
|
||||
int off = 0;
|
||||
for (int i = 0; i < _receivedIdentity.length; i++) {
|
||||
int len = _receivedIdentity[i].length;
|
||||
System.arraycopy(_receivedIdentity[i], 0, ident, off, len);
|
||||
off += len;
|
||||
}
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(ident);
|
||||
RouterIdentity peer = new RouterIdentity();
|
||||
try {
|
||||
peer.readBytes(in);
|
||||
|
||||
if (_receivedUnconfirmedIdentity == null)
|
||||
return; // either not yet recvd or bad ident
|
||||
if (_receivedSignature == null)
|
||||
return; // either not yet recvd or bad sig
|
||||
|
||||
byte signed[] = new byte[256+256 // X + Y
|
||||
+ _aliceIP.length + 2
|
||||
+ _bobIP.length + 2
|
||||
@ -373,7 +431,7 @@ class InboundEstablishState {
|
||||
+ 4 // signed on time
|
||||
];
|
||||
|
||||
off = 0;
|
||||
int off = 0;
|
||||
System.arraycopy(_receivedX, 0, signed, off, _receivedX.length);
|
||||
off += _receivedX.length;
|
||||
getSentY();
|
||||
@ -391,22 +449,15 @@ class InboundEstablishState {
|
||||
off += 4;
|
||||
DataHelper.toLong(signed, off, 4, _receivedSignedOnTime);
|
||||
Signature sig = new Signature(_receivedSignature);
|
||||
boolean ok = _context.dsa().verifySignature(sig, signed, peer.getSigningPublicKey());
|
||||
boolean ok = _context.dsa().verifySignature(sig, signed, _receivedUnconfirmedIdentity.getSigningPublicKey());
|
||||
if (ok) {
|
||||
// todo partial spoof detection - get peer.calculateHash(),
|
||||
// lookup in netdb locally, if not equal, fail?
|
||||
_receivedConfirmedIdentity = peer;
|
||||
_receivedConfirmedIdentity = _receivedUnconfirmedIdentity;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Signature failed from " + peer);
|
||||
_log.warn("Signature failed from " + _receivedUnconfirmedIdentity);
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Improperly formatted yet fully received ident", dfe);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Improperly formatted yet fully received ident", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
private void packetReceived() {
|
||||
|
@ -3,6 +3,7 @@ package net.i2p.router.transport.udp;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
@ -41,6 +42,7 @@ class OutboundEstablishState {
|
||||
private SessionKey _sessionKey;
|
||||
private SessionKey _macKey;
|
||||
private Signature _receivedSignature;
|
||||
// includes trailing padding to mod 16
|
||||
private byte[] _receivedEncryptedSignature;
|
||||
private byte[] _receivedIV;
|
||||
// SessionConfirmed messages
|
||||
@ -104,6 +106,7 @@ class OutboundEstablishState {
|
||||
/**
|
||||
* @param claimedAddress an IP/port based RemoteHostId, or null if unknown
|
||||
* @param remoteHostId non-null, == claimedAddress if direct, or a hash-based one if indirect
|
||||
* @param remotePeer must have supported sig type
|
||||
* @param introKey Bob's introduction key, as published in the netdb
|
||||
* @param addr non-null
|
||||
*/
|
||||
@ -247,8 +250,20 @@ class OutboundEstablishState {
|
||||
_alicePort = reader.readPort();
|
||||
_receivedRelayTag = reader.readRelayTag();
|
||||
_receivedSignedOnTime = reader.readSignedOnTime();
|
||||
_receivedEncryptedSignature = new byte[Signature.SIGNATURE_BYTES + 8];
|
||||
reader.readEncryptedSignature(_receivedEncryptedSignature, 0);
|
||||
// handle variable signature size
|
||||
SigType type = _remotePeer.getSigningPublicKey().getType();
|
||||
if (type == null) {
|
||||
// shouldn't happen, we only connect to supported peers
|
||||
fail();
|
||||
packetReceived();
|
||||
return;
|
||||
}
|
||||
int sigLen = type.getSigLen();
|
||||
int mod = sigLen % 16;
|
||||
int pad = (mod == 0) ? 0 : (16 - mod);
|
||||
int esigLen = sigLen + pad;
|
||||
_receivedEncryptedSignature = new byte[esigLen];
|
||||
reader.readEncryptedSignature(_receivedEncryptedSignature, 0, esigLen);
|
||||
_receivedIV = new byte[UDPPacket.IV_SIZE];
|
||||
reader.readIV(_receivedIV, 0);
|
||||
|
||||
@ -353,7 +368,9 @@ class OutboundEstablishState {
|
||||
* decrypt the signature (and subsequent pad bytes) with the
|
||||
* additional layer of encryption using the negotiated key along side
|
||||
* the packet's IV
|
||||
*
|
||||
* Caller must synch on this.
|
||||
* Only call this once! Decrypts in-place.
|
||||
*/
|
||||
private void decryptSignature() {
|
||||
if (_receivedEncryptedSignature == null) throw new NullPointerException("encrypted signature is null! this=" + this.toString());
|
||||
@ -361,11 +378,20 @@ class OutboundEstablishState {
|
||||
if (_receivedIV == null) throw new NullPointerException("IV is null!");
|
||||
_context.aes().decrypt(_receivedEncryptedSignature, 0, _receivedEncryptedSignature, 0,
|
||||
_sessionKey, _receivedIV, _receivedEncryptedSignature.length);
|
||||
byte signatureBytes[] = new byte[Signature.SIGNATURE_BYTES];
|
||||
System.arraycopy(_receivedEncryptedSignature, 0, signatureBytes, 0, Signature.SIGNATURE_BYTES);
|
||||
_receivedSignature = new Signature(signatureBytes);
|
||||
// handle variable signature size
|
||||
SigType type = _remotePeer.getSigningPublicKey().getType();
|
||||
// if type == null throws NPE
|
||||
int sigLen = type.getSigLen();
|
||||
int mod = sigLen % 16;
|
||||
if (mod != 0) {
|
||||
byte signatureBytes[] = new byte[sigLen];
|
||||
System.arraycopy(_receivedEncryptedSignature, 0, signatureBytes, 0, sigLen);
|
||||
_receivedSignature = new Signature(type, signatureBytes);
|
||||
} else {
|
||||
_receivedSignature = new Signature(type, _receivedEncryptedSignature);
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Decrypted received signature: " + Base64.encode(signatureBytes));
|
||||
_log.debug("Decrypted received signature: " + Base64.encode(_receivedSignature.getData()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -596,14 +596,22 @@ class PacketBuilder {
|
||||
off += 4;
|
||||
DataHelper.toLong(data, off, 4, state.getSentSignedOnTime());
|
||||
off += 4;
|
||||
System.arraycopy(state.getSentSignature().getData(), 0, data, off, Signature.SIGNATURE_BYTES);
|
||||
off += Signature.SIGNATURE_BYTES;
|
||||
// ok, we need another 8 bytes of random padding
|
||||
// (ok, this only gives us 63 bits, not 64)
|
||||
long l = _context.random().nextLong();
|
||||
if (l < 0) l = 0 - l;
|
||||
DataHelper.toLong(data, off, 8, l);
|
||||
off += 8;
|
||||
|
||||
// handle variable signature size
|
||||
Signature sig = state.getSentSignature();
|
||||
int siglen = sig.length();
|
||||
System.arraycopy(sig.getData(), 0, data, off, siglen);
|
||||
off += siglen;
|
||||
// ok, we need another few bytes of random padding
|
||||
int rem = siglen % 16;
|
||||
int padding;
|
||||
if (rem > 0) {
|
||||
padding = 16 - rem;
|
||||
_context.random().nextBytes(data, off, padding);
|
||||
off += padding;
|
||||
} else {
|
||||
padding = 0;
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
@ -612,9 +620,9 @@ class PacketBuilder {
|
||||
buf.append(" Bob: ").append(Addresses.toString(state.getReceivedOurIP(), externalPort));
|
||||
buf.append(" RelayTag: ").append(state.getSentRelayTag());
|
||||
buf.append(" SignedOn: ").append(state.getSentSignedOnTime());
|
||||
buf.append(" signature: ").append(Base64.encode(state.getSentSignature().getData()));
|
||||
buf.append(" signature: ").append(Base64.encode(sig.getData()));
|
||||
buf.append("\nRawCreated: ").append(Base64.encode(data, 0, off));
|
||||
buf.append("\nsignedTime: ").append(Base64.encode(data, off-8-Signature.SIGNATURE_BYTES-4, 4));
|
||||
buf.append("\nsignedTime: ").append(Base64.encode(data, off - padding - siglen - 4, 4));
|
||||
_log.debug(buf.toString());
|
||||
}
|
||||
|
||||
@ -623,7 +631,7 @@ class PacketBuilder {
|
||||
byte[] iv = SimpleByteCache.acquire(UDPPacket.IV_SIZE);
|
||||
_context.random().nextBytes(iv);
|
||||
|
||||
int encrWrite = Signature.SIGNATURE_BYTES + 8;
|
||||
int encrWrite = siglen + padding;
|
||||
int sigBegin = off - encrWrite;
|
||||
_context.aes().encrypt(data, sigBegin, data, sigBegin, state.getCipherKey(), iv, encrWrite);
|
||||
|
||||
@ -774,8 +782,11 @@ class PacketBuilder {
|
||||
DataHelper.toLong(data, off, 4, state.getSentSignedOnTime());
|
||||
off += 4;
|
||||
|
||||
// handle variable signature size
|
||||
// we need to pad this so we're at the encryption boundary
|
||||
int mod = (off + Signature.SIGNATURE_BYTES) & 0x0f;
|
||||
Signature sig = state.getSentSignature();
|
||||
int siglen = sig.length();
|
||||
int mod = (off + siglen) & 0x0f;
|
||||
if (mod != 0) {
|
||||
int paddingRequired = 16 - mod;
|
||||
// add an arbitrary number of 16byte pad blocks too ???
|
||||
@ -787,8 +798,8 @@ class PacketBuilder {
|
||||
// so trailing non-mod-16 data is ignored. That truncates the sig.
|
||||
|
||||
// BUG: NPE here if null signature
|
||||
System.arraycopy(state.getSentSignature().getData(), 0, data, off, Signature.SIGNATURE_BYTES);
|
||||
off += Signature.SIGNATURE_BYTES;
|
||||
System.arraycopy(sig.getData(), 0, data, off, siglen);
|
||||
off += siglen;
|
||||
} else {
|
||||
// We never get here (see above)
|
||||
|
||||
|
@ -203,9 +203,10 @@ class UDPPacketReader {
|
||||
return rv;
|
||||
}
|
||||
|
||||
public void readEncryptedSignature(byte target[], int targetOffset) {
|
||||
/** @param size the amount to be copied, including padding to mod 16 */
|
||||
public void readEncryptedSignature(byte target[], int targetOffset, int size) {
|
||||
int offset = readBodyOffset() + Y_LENGTH + 1 + readIPSize() + 2 + 4 + 4;
|
||||
System.arraycopy(_message, offset, target, targetOffset, Signature.SIGNATURE_BYTES + 8);
|
||||
System.arraycopy(_message, offset, target, targetOffset, size);
|
||||
}
|
||||
|
||||
public void readIV(byte target[], int targetOffset) {
|
||||
@ -239,7 +240,11 @@ class UDPPacketReader {
|
||||
System.arraycopy(_message, readOffset, target, targetOffset, len);
|
||||
}
|
||||
|
||||
/** read the time at which the signature was generated */
|
||||
/**
|
||||
* Read the time at which the signature was generated.
|
||||
* TODO must be completely in final fragment.
|
||||
* Time and sig cannot be split across fragments.
|
||||
*/
|
||||
public long readFinalFragmentSignedOnTime() {
|
||||
if (readCurrentFragmentNum() != readTotalFragmentNum()-1)
|
||||
throw new IllegalStateException("This is not the final fragment");
|
||||
@ -247,12 +252,19 @@ class UDPPacketReader {
|
||||
return DataHelper.fromLong(_message, readOffset, 4);
|
||||
}
|
||||
|
||||
/** read the signature from the final sessionConfirmed packet */
|
||||
public void readFinalSignature(byte target[], int targetOffset) {
|
||||
/**
|
||||
* Read the signature from the final sessionConfirmed packet.
|
||||
* TODO must be completely in final fragment.
|
||||
* Time and sig cannot be split across fragments.
|
||||
* @param size not including padding
|
||||
*/
|
||||
public void readFinalSignature(byte target[], int targetOffset, int size) {
|
||||
if (readCurrentFragmentNum() != readTotalFragmentNum()-1)
|
||||
throw new IllegalStateException("This is not the final fragment");
|
||||
int readOffset = _payloadBeginOffset + _payloadLength - Signature.SIGNATURE_BYTES;
|
||||
System.arraycopy(_message, readOffset, target, targetOffset, Signature.SIGNATURE_BYTES);
|
||||
int readOffset = _payloadBeginOffset + _payloadLength - size;
|
||||
if (readOffset < readBodyOffset() + (1 + 2 + 4))
|
||||
throw new IllegalStateException("Sig split across fragments");
|
||||
System.arraycopy(_message, readOffset, target, targetOffset, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1550,6 +1550,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check for supported sig type
|
||||
if (toAddress.getIdentity().getSigningPublicKey().getType() == null) {
|
||||
markUnreachable(to);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!allowConnection())
|
||||
return _cachedBid[TRANSIENT_FAIL_BID];
|
||||
|
||||
|
Reference in New Issue
Block a user