SSU: Handle RI sig types

TransportManager: Banlist unsupported RI sig types
This commit is contained in:
zzz
2014-08-24 14:54:17 +00:00
parent 54563b0b42
commit 04ad7de2e1
7 changed files with 176 additions and 60 deletions

View File

@ -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"));
}

View File

@ -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");

View File

@ -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() {

View File

@ -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()));
}
/**

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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];