forked from I2P_Developers/i2p.i2p
SSU2: Stubs for relay and peer test (phase 2)
This commit is contained in:
@@ -638,27 +638,21 @@ class EstablishmentManager {
|
||||
}
|
||||
|
||||
if (isNew) {
|
||||
/**** TODO
|
||||
// Don't offer to relay to privileged ports.
|
||||
// Only offer for an IPv4 session.
|
||||
// TODO if already we have their RI, only offer if they need it (no 'C' cap)
|
||||
// if extended options, only if they asked for it
|
||||
if (state.isIntroductionRequested() &&
|
||||
state.getSentPort() >= 1024 &&
|
||||
_transport.canIntroduce(state.getSentIP().length == 16)) {
|
||||
// ensure > 0
|
||||
long tag = 1 + _context.random().nextLong(MAX_TAG_VALUE);
|
||||
state.setSentRelayTag(tag);
|
||||
} else {
|
||||
// we got an IB even though we were firewalled, hidden, not high cap, etc.
|
||||
}
|
||||
****/
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Received NEW session/token request " + state);
|
||||
} else {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Receive DUP session/token request from: " + state);
|
||||
}
|
||||
// call for both Session and Token request, why not
|
||||
if (SSU2Util.ENABLE_RELAY &&
|
||||
state.isIntroductionRequested() &&
|
||||
state.getSentRelayTag() == 0 && // only set once
|
||||
state.getSentPort() >= 1024 &&
|
||||
_transport.canIntroduce(state.getSentIP().length == 16)) {
|
||||
long tag = 1 + _context.random().nextLong(MAX_TAG_VALUE);
|
||||
state.setSentRelayTag(tag);
|
||||
}
|
||||
notifyActivity();
|
||||
}
|
||||
|
||||
@@ -1059,7 +1053,6 @@ class EstablishmentManager {
|
||||
state.getSentIP(), state.getSentPort(), remote.calculateHash(), false, state.getRTT());
|
||||
peer.setCurrentCipherKey(state.getCipherKey());
|
||||
peer.setCurrentMACKey(state.getMACKey());
|
||||
peer.setTheyRelayToUsAs(state.getReceivedRelayTag());
|
||||
int mtu = state.getRemoteAddress().getMTU();
|
||||
if (mtu > 0)
|
||||
peer.setHisMTU(mtu);
|
||||
@@ -1068,6 +1061,7 @@ class EstablishmentManager {
|
||||
// OES2 sets PS2 MTU
|
||||
peer = state2.getPeerState();
|
||||
}
|
||||
peer.setTheyRelayToUsAs(state.getReceivedRelayTag());
|
||||
// 0 is the default
|
||||
//peer.setWeRelayToThemAs(0);
|
||||
|
||||
|
||||
@@ -63,8 +63,8 @@ class InboundEstablishState {
|
||||
private final Queue<OutNetMessage> _queuedMessages;
|
||||
// count for backoff
|
||||
protected int _createdSentCount;
|
||||
// default true
|
||||
protected boolean _introductionRequested = true;
|
||||
// default true for SSU 1, false for SSU 2
|
||||
protected boolean _introductionRequested;
|
||||
|
||||
protected int _rtt;
|
||||
|
||||
@@ -127,6 +127,7 @@ class InboundEstablishState {
|
||||
_establishBegin = ctx.clock().now();
|
||||
_keyBuilder = dh;
|
||||
_queuedMessages = new LinkedBlockingQueue<OutNetMessage>();
|
||||
_introductionRequested = true;
|
||||
receiveSessionRequest(req);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,6 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
_rcvHeaderEncryptKey1 = introKey;
|
||||
//_sendHeaderEncryptKey2 set below
|
||||
//_rcvHeaderEncryptKey2 set below
|
||||
_introductionRequested = false; // todo
|
||||
int off = pkt.getOffset();
|
||||
int len = pkt.getLength();
|
||||
byte data[] = pkt.getData();
|
||||
@@ -321,12 +320,43 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
}
|
||||
|
||||
public void gotRelayTagRequest() {
|
||||
if (!ENABLE_RELAY)
|
||||
return;
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Got relay tag request");
|
||||
_introductionRequested = true;
|
||||
}
|
||||
|
||||
public void gotRelayTag(long tag) {
|
||||
throw new IllegalStateException("Relay tag in Handshake");
|
||||
// shouldn't happen for inbound
|
||||
}
|
||||
|
||||
public void gotRelayRequest(byte[] data) {
|
||||
if (!ENABLE_RELAY)
|
||||
return;
|
||||
if (_receivedConfirmedIdentity == null)
|
||||
throw new IllegalStateException("RI must be first");
|
||||
}
|
||||
|
||||
public void gotRelayResponse(int status, byte[] data) {
|
||||
if (!ENABLE_RELAY)
|
||||
return;
|
||||
if (_receivedConfirmedIdentity == null)
|
||||
throw new IllegalStateException("RI must be first");
|
||||
}
|
||||
|
||||
public void gotRelayIntro(Hash aliceHash, byte[] data) {
|
||||
if (!ENABLE_RELAY)
|
||||
return;
|
||||
if (_receivedConfirmedIdentity == null)
|
||||
throw new IllegalStateException("RI must be first");
|
||||
}
|
||||
|
||||
public void gotPeerTest(int msg, int status, Hash h, byte[] data) {
|
||||
if (!ENABLE_PEER_TEST)
|
||||
return;
|
||||
if (_receivedConfirmedIdentity == null)
|
||||
throw new IllegalStateException("RI must be first");
|
||||
}
|
||||
|
||||
public void gotToken(long token, long expires) {
|
||||
|
||||
@@ -126,7 +126,7 @@ class IntroductionManager {
|
||||
public void add(PeerState peer) {
|
||||
if (peer == null) return;
|
||||
// Skip SSU2 until we have support for relay
|
||||
if (peer.getVersion() != 1)
|
||||
if (peer.getVersion() != 1 && !SSU2Util.ENABLE_RELAY)
|
||||
return;
|
||||
// let's not use an introducer on a privileged port, sounds like trouble
|
||||
if (!TransportUtil.isValidPort(peer.getRemotePort()))
|
||||
|
||||
@@ -37,7 +37,7 @@ class OutboundEstablishState {
|
||||
private byte _receivedY[];
|
||||
protected byte _aliceIP[];
|
||||
protected int _alicePort;
|
||||
private long _receivedRelayTag;
|
||||
protected long _receivedRelayTag;
|
||||
private long _receivedSignedOnTime;
|
||||
private SessionKey _sessionKey;
|
||||
private SessionKey _macKey;
|
||||
|
||||
@@ -16,6 +16,7 @@ import net.i2p.crypto.HKDF;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
@@ -215,8 +216,27 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
}
|
||||
|
||||
public void gotRelayTag(long tag) {
|
||||
if (!ENABLE_RELAY)
|
||||
return;
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Got relay tag " + tag);
|
||||
_receivedRelayTag = tag;
|
||||
}
|
||||
|
||||
public void gotRelayRequest(byte[] data) {
|
||||
// won't be called, SSU2Payload will throw
|
||||
}
|
||||
|
||||
public void gotRelayResponse(int status, byte[] data) {
|
||||
// won't be called, SSU2Payload will throw
|
||||
}
|
||||
|
||||
public void gotRelayIntro(Hash aliceHash, byte[] data) {
|
||||
// won't be called, SSU2Payload will throw
|
||||
}
|
||||
|
||||
public void gotPeerTest(int msg, int status, Hash h, byte[] data) {
|
||||
// won't be called, SSU2Payload will throw
|
||||
}
|
||||
|
||||
public void gotToken(long token, long expires) {
|
||||
|
||||
@@ -386,9 +386,41 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
|
||||
}
|
||||
|
||||
public void gotRelayTagRequest() {
|
||||
if (!ENABLE_RELAY)
|
||||
return;
|
||||
}
|
||||
|
||||
public void gotRelayTag(long tag) {
|
||||
if (!ENABLE_RELAY)
|
||||
return;
|
||||
long old = getTheyRelayToUsAs();
|
||||
if (old != 0) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Got new tag " + tag + " but had previous tag " + old + " on " + this);
|
||||
return;
|
||||
}
|
||||
setTheyRelayToUsAs(tag);
|
||||
_transport.getIntroManager().add(this);
|
||||
}
|
||||
|
||||
public void gotRelayRequest(byte[] data) {
|
||||
if (!ENABLE_RELAY)
|
||||
return;
|
||||
}
|
||||
|
||||
public void gotRelayResponse(int status, byte[] data) {
|
||||
if (!ENABLE_RELAY)
|
||||
return;
|
||||
}
|
||||
|
||||
public void gotRelayIntro(Hash aliceHash, byte[] data) {
|
||||
if (!ENABLE_RELAY)
|
||||
return;
|
||||
}
|
||||
|
||||
public void gotPeerTest(int msg, int status, Hash h, byte[] data) {
|
||||
if (!ENABLE_PEER_TEST)
|
||||
return;
|
||||
}
|
||||
|
||||
public void gotToken(long token, long expires) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.List;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.data.i2np.I2NPMessageException;
|
||||
import net.i2p.data.i2np.I2NPMessageImpl;
|
||||
@@ -94,6 +95,30 @@ class SSU2Payload {
|
||||
|
||||
public void gotRelayTag(long tag);
|
||||
|
||||
/**
|
||||
* @param data excludes flag, includes signature
|
||||
*/
|
||||
public void gotRelayRequest(byte[] data);
|
||||
|
||||
/**
|
||||
* @param status 0 = accept, 1-255 = reject
|
||||
* @param data excludes flag, includes signature
|
||||
*/
|
||||
public void gotRelayResponse(int status, byte[] data);
|
||||
|
||||
/**
|
||||
* @param data excludes flag, includes signature
|
||||
*/
|
||||
public void gotRelayIntro(Hash aliceHash, byte[] data);
|
||||
|
||||
/**
|
||||
* @param msg 1-7
|
||||
* @param status 0 = accept, 1-255 = reject
|
||||
* @param h Alice or Charlie hash
|
||||
* @param data excludes flag, includes signature
|
||||
*/
|
||||
public void gotPeerTest(int msg, int status, Hash h, byte[] data);
|
||||
|
||||
public void gotToken(long token, long expires);
|
||||
|
||||
/**
|
||||
@@ -114,6 +139,7 @@ class SSU2Payload {
|
||||
/**
|
||||
* Incoming payload. Calls the callback for each received block.
|
||||
*
|
||||
* @param isHandshake true for Token Req, Retry, Sess Req, Sess Created; false for Sess Confirmed
|
||||
* @return number of blocks processed
|
||||
* @throws IOException on major errors
|
||||
* @throws DataFormatException on parsing of individual blocks
|
||||
@@ -256,6 +282,55 @@ class SSU2Payload {
|
||||
cb.gotRelayTag(tag);
|
||||
break;
|
||||
|
||||
case BLOCK_RELAYREQ: {
|
||||
if (isHandshake)
|
||||
throw new IOException("Illegal block in handshake: " + type);
|
||||
if (len < 61) // 21 byte data w/ IPv4 + 40 byte DSA sig
|
||||
throw new IOException("Bad length for RELAYREQ: " + len);
|
||||
byte[] data = new byte[len - 1]; // skip flag
|
||||
System.arraycopy(payload, i + 1, data, 0, len - 1);
|
||||
cb.gotRelayRequest(data);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLOCK_RELAYRESP: {
|
||||
if (isHandshake)
|
||||
throw new IOException("Illegal block in handshake: " + type);
|
||||
if (len < 62) // 22 byte data w/ IPv4 + 40 byte DSA sig
|
||||
throw new IOException("Bad length for RELAYRESP: " + len);
|
||||
int resp = payload[i + 1] & 0xff; // skip flag
|
||||
byte[] data = new byte[len - 2];
|
||||
System.arraycopy(payload, i + 2, data, 0, len - 2);
|
||||
cb.gotRelayResponse(resp, data);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLOCK_RELAYINTRO: {
|
||||
if (isHandshake)
|
||||
throw new IOException("Illegal block in handshake: " + type);
|
||||
if (len < 93) // 32 byte hash + 21 byte data w/ IPv4 + 40 byte DSA sig
|
||||
throw new IOException("Bad length for RELAYINTRO: " + len);
|
||||
Hash h = Hash.create(payload, i + 1); // skip flag
|
||||
byte[] data = new byte[len - (1 + Hash.HASH_LENGTH)]; // skip flag
|
||||
System.arraycopy(payload, i + 1 + Hash.HASH_LENGTH, data, 0, data.length);
|
||||
cb.gotRelayIntro(h, data);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLOCK_PEERTEST: {
|
||||
if (isHandshake)
|
||||
throw new IOException("Illegal block in handshake: " + type);
|
||||
if (len < 92) // 32 byte hash + 20 byte data w/ IPv4 + 40 byte DSA sig
|
||||
throw new IOException("Bad length for PEERTEST: " + len);
|
||||
int mnum = payload[i] & 0xff;
|
||||
int resp = payload[i + 1] & 0xff;
|
||||
Hash h = Hash.create(payload, i + 3); // skip flag
|
||||
byte[] data = new byte[len - (3 + Hash.HASH_LENGTH)];
|
||||
System.arraycopy(payload, i + 3 + Hash.HASH_LENGTH, data, 0, data.length);
|
||||
cb.gotPeerTest(mnum, resp, h, data);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLOCK_NEWTOKEN:
|
||||
if (len < 12)
|
||||
throw new IOException("Bad length for NEWTOKEN: " + len);
|
||||
@@ -644,6 +719,78 @@ class SSU2Payload {
|
||||
}
|
||||
}
|
||||
|
||||
public static class RelayRequestBlock extends Block {
|
||||
private final byte[] d;
|
||||
|
||||
public RelayRequestBlock(byte[] data) {
|
||||
super(BLOCK_RELAYREQ);
|
||||
d = data;
|
||||
}
|
||||
|
||||
public int getDataLength() {
|
||||
return d.length;
|
||||
}
|
||||
|
||||
public int writeData(byte[] tgt, int off) {
|
||||
System.arraycopy(d, 0, tgt, off, d.length);
|
||||
return off + d.length;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RelayResponseBlock extends Block {
|
||||
private final byte[] d;
|
||||
|
||||
public RelayResponseBlock(byte[] data) {
|
||||
super(BLOCK_RELAYRESP);
|
||||
d = data;
|
||||
}
|
||||
|
||||
public int getDataLength() {
|
||||
return d.length;
|
||||
}
|
||||
|
||||
public int writeData(byte[] tgt, int off) {
|
||||
System.arraycopy(d, 0, tgt, off, d.length);
|
||||
return off + d.length;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RelayIntroBlock extends Block {
|
||||
private final byte[] d;
|
||||
|
||||
public RelayIntroBlock(byte[] data) {
|
||||
super(BLOCK_RELAYINTRO);
|
||||
d = data;
|
||||
}
|
||||
|
||||
public int getDataLength() {
|
||||
return d.length;
|
||||
}
|
||||
|
||||
public int writeData(byte[] tgt, int off) {
|
||||
System.arraycopy(d, 0, tgt, off, d.length);
|
||||
return off + d.length;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PeerTestBlock extends Block {
|
||||
private final byte[] d;
|
||||
|
||||
public PeerTestBlock(byte[] data) {
|
||||
super(BLOCK_PEERTEST);
|
||||
d = data;
|
||||
}
|
||||
|
||||
public int getDataLength() {
|
||||
return d.length;
|
||||
}
|
||||
|
||||
public int writeData(byte[] tgt, int off) {
|
||||
System.arraycopy(d, 0, tgt, off, d.length);
|
||||
return off + d.length;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NewTokenBlock extends Block {
|
||||
private final long t, e;
|
||||
|
||||
|
||||
@@ -3,6 +3,12 @@ package net.i2p.router.transport.udp;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.EncType;
|
||||
import net.i2p.crypto.HKDF;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
|
||||
/**
|
||||
* SSU2 Utils and constants
|
||||
@@ -12,6 +18,11 @@ import net.i2p.crypto.HKDF;
|
||||
final class SSU2Util {
|
||||
public static final int PROTOCOL_VERSION = 2;
|
||||
|
||||
// features
|
||||
public static final boolean ENABLE_RELAY = false;
|
||||
public static final boolean ENABLE_PEER_TEST = false;
|
||||
public static final boolean ENABLE_PATH_CHALLENGE = false;
|
||||
|
||||
// lengths
|
||||
/** 32 */
|
||||
public static final int KEY_LEN = EncType.ECIES_X25519.getPubkeyLen();
|
||||
@@ -87,6 +98,12 @@ final class SSU2Util {
|
||||
public static final byte[] ZEROLEN = new byte[0];
|
||||
public static final byte[] ZEROKEY = new byte[KEY_LEN];
|
||||
|
||||
// relay and peer test
|
||||
public static final byte[] RELAY_REQUEST_PROLOGUE = DataHelper.getASCII("RelayRequestData");
|
||||
public static final byte[] RELAY_RESPONSE_PROLOGUE = DataHelper.getASCII("RelayAgreementOK");
|
||||
public static final byte[] PEER_TEST_PROLOGUE = DataHelper.getASCII("PeerTestValidate");
|
||||
|
||||
|
||||
private SSU2Util() {}
|
||||
|
||||
/**
|
||||
@@ -98,4 +115,37 @@ final class SSU2Util {
|
||||
hkdf.calculate(key, ZEROLEN, info, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign the relay or peer test data, using
|
||||
* the prologue and hash as the initial data,
|
||||
* and then the provided data.
|
||||
*
|
||||
* @return null on failure
|
||||
*/
|
||||
public static Signature sign(I2PAppContext ctx, byte[] prologue, Hash h, byte[] data, SigningPrivateKey spk) {
|
||||
byte[] buf = new byte[prologue.length + Hash.HASH_LENGTH + data.length];
|
||||
System.arraycopy(prologue, 0, buf, 0, prologue.length);
|
||||
System.arraycopy(h.getData(), 0, buf, prologue.length, Hash.HASH_LENGTH);
|
||||
System.arraycopy(data, 0, buf, prologue.length + Hash.HASH_LENGTH, data.length);
|
||||
return ctx.dsa().sign(buf, spk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the signed relay or peer test data, using
|
||||
* the prologue and hash as the initial data,
|
||||
* and then the provided data which ends with a signature of the specified type.
|
||||
*/
|
||||
public static boolean validateSig(I2PAppContext ctx, byte[] prologue, Hash h, byte[] data, SigningPublicKey spk) {
|
||||
SigType type = spk.getType();
|
||||
int siglen = type.getSigLen();
|
||||
byte[] buf = new byte[prologue.length + Hash.HASH_LENGTH + data.length - siglen];
|
||||
System.arraycopy(prologue, 0, buf, 0, prologue.length);
|
||||
System.arraycopy(h.getData(), 0, buf, prologue.length, Hash.HASH_LENGTH);
|
||||
System.arraycopy(data, 0, buf, prologue.length + Hash.HASH_LENGTH, data.length - siglen);
|
||||
byte[] bsig = new byte[siglen];
|
||||
System.arraycopy(data, data.length - siglen, bsig, 0, siglen);
|
||||
Signature sig = new Signature(type, bsig);
|
||||
return ctx.dsa().verifySignature(sig, buf, spk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3433,6 +3433,20 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
return _packetBuilder2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.54
|
||||
*/
|
||||
IntroductionManager getIntroManager() {
|
||||
return _introManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.54
|
||||
*/
|
||||
PeerTestManager getPeerTestManager() {
|
||||
return _testManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
* @deprecated as of 0.9.31
|
||||
|
||||
Reference in New Issue
Block a user