forked from I2P_Developers/i2p.i2p
SSU2: Relay WIP part 3
Put alice hash in relay intro as Bob Fix relay intro/response generation and signing Add token to relay response as Charlie Store relay nonce as Bob Handle relay response as Bob and forward to Alice Stub out handling relay response as Alice
This commit is contained in:
@@ -1296,6 +1296,8 @@ class EstablishmentManager {
|
||||
|
||||
/**
|
||||
* We are Alice, we sent a RelayRequest to Bob and got a response back.
|
||||
*
|
||||
* SSU 1 only.
|
||||
*/
|
||||
void receiveRelayResponse(RemoteHostId bob, UDPPacketReader reader) {
|
||||
long nonce = reader.getRelayResponseReader().readNonce();
|
||||
@@ -1353,6 +1355,18 @@ class EstablishmentManager {
|
||||
notifyActivity();
|
||||
}
|
||||
|
||||
/**
|
||||
* We are Alice, we sent a RelayRequest to Bob and got a RelayResponse back.
|
||||
*
|
||||
* SSU 2 only.
|
||||
*
|
||||
* @param data including token if code == 0
|
||||
* @since 0.9.55
|
||||
*/
|
||||
void receiveRelayResponse(PeerState2 bob, long nonce, int code, byte[] data) {
|
||||
// lookup nonce, determine who signed, validate sig, send SessionRequest if code == 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from UDPReceiver.
|
||||
* Accelerate response to RelayResponse if we haven't sent it yet.
|
||||
|
||||
@@ -86,6 +86,8 @@ class IntroductionManager {
|
||||
private final Map<Long, PeerState> _outbound;
|
||||
/** map of relay tag to PeerState who have given us introduction tags */
|
||||
private final Map<Long, PeerState> _inbound;
|
||||
/** map of relay nonce to alice PeerState who requested it */
|
||||
private final Map<Long, PeerState2> _nonceToAlice;
|
||||
private final Set<InetAddress> _recentHolePunches;
|
||||
private long _lastHolePunchClean;
|
||||
|
||||
@@ -117,6 +119,7 @@ class IntroductionManager {
|
||||
_builder2 = transport.getBuilder2();
|
||||
_outbound = new ConcurrentHashMap<Long, PeerState>(MAX_OUTBOUND);
|
||||
_inbound = new ConcurrentHashMap<Long, PeerState>(MAX_INBOUND);
|
||||
_nonceToAlice = (_builder2 != null) ? new ConcurrentHashMap<Long, PeerState2>(MAX_INBOUND) : null;
|
||||
_recentHolePunches = new HashSet<InetAddress>(16);
|
||||
ctx.statManager().createRateStat("udp.receiveRelayIntro", "How often we get a relayed request for us to talk to someone?", "udp", UDPTransport.RATES);
|
||||
ctx.statManager().createRateStat("udp.receiveRelayRequest", "How often we receive a good request to relay to someone else?", "udp", UDPTransport.RATES);
|
||||
@@ -669,7 +672,6 @@ class IntroductionManager {
|
||||
* @since 0.9.55
|
||||
*/
|
||||
void receiveRelayRequest(PeerState2 alice, byte[] data) {
|
||||
long tag = DataHelper.fromLong(data, 4, 4);
|
||||
long time = DataHelper.fromLong(data, 8, 4) * 1000;
|
||||
long now = _context.clock().now();
|
||||
long skew = time - now;
|
||||
@@ -684,6 +686,8 @@ class IntroductionManager {
|
||||
_log.warn("Bad relay req version " + ver + " from " + alice);
|
||||
return;
|
||||
}
|
||||
long nonce = DataHelper.fromLong(data, 0, 4);
|
||||
long tag = DataHelper.fromLong(data, 4, 4);
|
||||
PeerState charlie = _outbound.get(Long.valueOf(tag));
|
||||
RouterInfo aliceRI = null;
|
||||
int rcode;
|
||||
@@ -700,7 +704,15 @@ class IntroductionManager {
|
||||
SigningPublicKey spk = aliceRI.getIdentity().getSigningPublicKey();
|
||||
if (SSU2Util.validateSig(_context, SSU2Util.RELAY_REQUEST_PROLOGUE,
|
||||
_context.routerHash(), charlie.getRemotePeer(), data, spk)) {
|
||||
rcode = SSU2Util.RELAY_ACCEPT;
|
||||
// save tag-to-alice mapping so we can forward the reply from charlie
|
||||
PeerState2 old = _nonceToAlice.putIfAbsent(Long.valueOf(nonce), alice);
|
||||
if (old != null && !old.equals(alice)) {
|
||||
// dup tag
|
||||
rcode = SSU2Util.RELAY_REJECT_BOB_UNSPEC;
|
||||
} else {
|
||||
rcode = SSU2Util.RELAY_ACCEPT;
|
||||
}
|
||||
// TODO add timer to remove from _nonceToAlice
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Signature failed relay intro\n" + aliceRI);
|
||||
@@ -721,17 +733,21 @@ class IntroductionManager {
|
||||
dbsm.setEntry(aliceRI);
|
||||
dbsm.setMessageExpiration(now + 10*1000);
|
||||
_transport.send(dbsm, charlie);
|
||||
packet = _builder2.buildRelayIntro(data, (PeerState2) charlie);
|
||||
// put alice hash in intro data
|
||||
byte[] idata = new byte[1 + Hash.HASH_LENGTH + data.length];
|
||||
//idata[0] = 0; // flag
|
||||
System.arraycopy(alice.getRemotePeer().getData(), 0, idata, 1, Hash.HASH_LENGTH);
|
||||
System.arraycopy(data, 0, idata, 1 + Hash.HASH_LENGTH, data.length);
|
||||
packet = _builder2.buildRelayIntro(idata, (PeerState2) charlie);
|
||||
} else {
|
||||
// send rejection to Alice
|
||||
SigningPrivateKey spk = _context.keyManager().getSigningPrivateKey();
|
||||
long nonce = DataHelper.fromLong(data, 0, 4);
|
||||
int iplen = data[13] & 0xff;
|
||||
int testPort = (int) DataHelper.fromLong(data, 14, 2);
|
||||
byte[] testIP = new byte[iplen - 2];
|
||||
System.arraycopy(data, 16, testIP, 0, iplen - 2);
|
||||
data = SSU2Util.createRelayResponseData(_context, _context.routerHash(), rcode,
|
||||
nonce, testIP, testPort, spk);
|
||||
nonce, testIP, testPort, spk, 0);
|
||||
if (data == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("sig fail");
|
||||
@@ -828,9 +844,17 @@ class IntroductionManager {
|
||||
|
||||
// generate our signed data
|
||||
// we sign it even if rejecting, not required though
|
||||
long token;
|
||||
if (rcode == SSU2Util.RELAY_ACCEPT) {
|
||||
RemoteHostId aliceID = new RemoteHostId(testIP, testPort);
|
||||
EstablishmentManager.Token tok = _transport.getEstablisher().getInboundToken(aliceID);
|
||||
token = tok.token;
|
||||
} else {
|
||||
token = 0;
|
||||
}
|
||||
SigningPrivateKey spk = _context.keyManager().getSigningPrivateKey();
|
||||
data = SSU2Util.createRelayResponseData(_context, bob.getRemotePeer(), rcode,
|
||||
nonce, testIP, testPort, spk);
|
||||
nonce, testIP, testPort, spk, token);
|
||||
if (data == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("sig fail");
|
||||
@@ -864,6 +888,40 @@ class IntroductionManager {
|
||||
* @since 0.9.55
|
||||
*/
|
||||
void receiveRelayResponse(PeerState2 peer, int status, byte[] data) {
|
||||
long nonce = DataHelper.fromLong(data, 0, 4);
|
||||
long time = DataHelper.fromLong(data, 4, 4) * 1000;
|
||||
long now = _context.clock().now();
|
||||
long skew = time - now;
|
||||
if (skew > MAX_SKEW || skew < 0 - MAX_SKEW) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Too skewed for relay resp from " + peer);
|
||||
return;
|
||||
}
|
||||
int ver = data[8] & 0xff;
|
||||
if (ver != 2) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Bad relay intro version " + ver + " from " + peer);
|
||||
return;
|
||||
}
|
||||
// Look up nonce to determine if we are Alice or Bob
|
||||
PeerState2 alice = _nonceToAlice.remove(Long.valueOf(nonce));
|
||||
if (alice != null) {
|
||||
// We are Bob, send to Alice
|
||||
// We don't check the signature here
|
||||
byte[] idata = new byte[2 + data.length];
|
||||
//idata[0] = 0; // flag
|
||||
idata[1] = (byte) status;
|
||||
System.arraycopy(data, 0, idata, 2, data.length);
|
||||
UDPPacket packet = _builder2.buildRelayResponse(idata, alice);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Send relay response " + " nonce " + nonce + " to " + alice);
|
||||
_transport.send(packet);
|
||||
} else {
|
||||
// We are Alice, give to EstablishmentManager to check sig and process
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Got relay response " + " nonce " + nonce + " from " + peer);
|
||||
_transport.getEstablisher().receiveRelayResponse(peer, nonce, status, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -723,6 +723,7 @@ class PacketBuilder2 {
|
||||
* From Alice to Bob.
|
||||
* In-session.
|
||||
*
|
||||
* @param signedData flag + signed data
|
||||
* @return null on failure
|
||||
*/
|
||||
UDPPacket buildRelayRequest(byte[] signedData, PeerState2 bob) {
|
||||
@@ -737,6 +738,7 @@ class PacketBuilder2 {
|
||||
* From Bob to Charlie.
|
||||
* In-session.
|
||||
*
|
||||
* @param signedData flag + alice hash + signed data
|
||||
* @return null on failure
|
||||
*/
|
||||
UDPPacket buildRelayIntro(byte[] signedData, PeerState2 charlie) {
|
||||
@@ -750,6 +752,7 @@ class PacketBuilder2 {
|
||||
* From Charlie to Bob or Bob to Alice.
|
||||
* In-session.
|
||||
*
|
||||
* @param signedData flag + response code + signed data + optional token
|
||||
* @param state Alice or Bob
|
||||
* @return null on failure
|
||||
*/
|
||||
|
||||
@@ -227,22 +227,25 @@ final class SSU2Util {
|
||||
public static byte[] createRelayRequestData(I2PAppContext ctx, Hash h, Hash h2,
|
||||
long nonce, long tag, byte[] ip, int port,
|
||||
SigningPrivateKey spk) {
|
||||
int datalen = 17 + ip.length;
|
||||
byte[] data = new byte[datalen + spk.getType().getSigLen()];
|
||||
//data[0] = 0; // flag
|
||||
DataHelper.toLong(data, 1, 4, nonce);
|
||||
DataHelper.toLong(data, 5, 4, tag);
|
||||
DataHelper.toLong(data, 9, 4, ctx.clock().now() / 1000);
|
||||
data[13] = 2; // version
|
||||
data[14] = (byte) (ip.length + 2);
|
||||
DataHelper.toLong(data, 15, 2, port);
|
||||
System.arraycopy(ip, 0, data, 17, ip.length);
|
||||
int datalen = 16 + ip.length;
|
||||
byte[] data = new byte[datalen];
|
||||
DataHelper.toLong(data, 0, 4, nonce);
|
||||
DataHelper.toLong(data, 4, 4, tag);
|
||||
DataHelper.toLong(data, 8, 4, ctx.clock().now() / 1000);
|
||||
data[12] = 2; // version
|
||||
data[13] = (byte) (ip.length + 2);
|
||||
DataHelper.toLong(data, 14, 2, port);
|
||||
System.arraycopy(ip, 0, data, 16, ip.length);
|
||||
Signature sig = sign(ctx, RELAY_REQUEST_PROLOGUE, h, h2, data, datalen, spk);
|
||||
if (sig == null)
|
||||
return null;
|
||||
int len = 1 + datalen + spk.getType().getSigLen();
|
||||
byte[] rv = new byte[len];
|
||||
//rv[0] = 0; // flag
|
||||
System.arraycopy(data, 0, rv, 1, data.length);
|
||||
byte[] s = sig.getData();
|
||||
System.arraycopy(s, 0, data, datalen, s.length);
|
||||
return data;
|
||||
System.arraycopy(s, 0, rv, 1 + datalen, s.length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,28 +253,36 @@ final class SSU2Util {
|
||||
*
|
||||
* @param h Bob hash to be included in sig, not included in data
|
||||
* @param ip non-null
|
||||
* @param token if nonzero, append it
|
||||
* @return null on failure
|
||||
* @since 0.9.55
|
||||
*/
|
||||
public static byte[] createRelayResponseData(I2PAppContext ctx, Hash h, int code,
|
||||
long nonce, byte[] ip, int port,
|
||||
SigningPrivateKey spk) {
|
||||
int datalen = 14 + ip.length;
|
||||
byte[] data = new byte[datalen + spk.getType().getSigLen()];
|
||||
//data[0] = 0; // flag
|
||||
data[1] = (byte) code;
|
||||
DataHelper.toLong(data, 2, 4, nonce);
|
||||
DataHelper.toLong(data, 6, 4, ctx.clock().now() / 1000);
|
||||
data[10] = 2; // version
|
||||
data[11] = (byte) (ip.length + 2);
|
||||
DataHelper.toLong(data, 12, 2, port);
|
||||
System.arraycopy(ip, 0, data, 14, ip.length);
|
||||
SigningPrivateKey spk, long token) {
|
||||
int datalen = 12 + ip.length;
|
||||
byte[] data = new byte[datalen];
|
||||
DataHelper.toLong(data, 0, 4, nonce);
|
||||
DataHelper.toLong(data, 4, 4, ctx.clock().now() / 1000);
|
||||
data[8] = 2; // version
|
||||
data[9] = (byte) (ip.length + 2);
|
||||
DataHelper.toLong(data, 10, 2, port);
|
||||
System.arraycopy(ip, 0, data, 12, ip.length);
|
||||
Signature sig = sign(ctx, RELAY_RESPONSE_PROLOGUE, h, null, data, datalen, spk);
|
||||
if (sig == null)
|
||||
return null;
|
||||
int len = 2 + datalen + spk.getType().getSigLen();
|
||||
if (token != 0)
|
||||
len += 8;
|
||||
byte[] rv = new byte[len];
|
||||
//rv[0] = 0; // flag
|
||||
rv[1] = (byte) code;
|
||||
System.arraycopy(data, 0, rv, 2, data.length);
|
||||
byte[] s = sig.getData();
|
||||
System.arraycopy(s, 0, data, datalen, s.length);
|
||||
return data;
|
||||
System.arraycopy(s, 0, rv, 2 + datalen, s.length);
|
||||
if (token != 0)
|
||||
DataHelper.toLong8(rv, 2 + datalen + s.length, token);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user