forked from I2P_Developers/i2p.i2p
SSU2: Token Req. and Retry fixes
Fix Token Request and Retry payload generation Implement Token Request and Retry payload decryption Decrypt payloads in-place Change from numbers to constants
This commit is contained in:
@@ -9,6 +9,7 @@ import java.net.UnknownHostException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.List;
|
||||
|
||||
import com.southernstorm.noise.protocol.ChaChaPolyCipherState;
|
||||
import com.southernstorm.noise.protocol.CipherState;
|
||||
import com.southernstorm.noise.protocol.CipherStatePair;
|
||||
import com.southernstorm.noise.protocol.HandshakeState;
|
||||
@@ -23,6 +24,7 @@ import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import static net.i2p.router.transport.udp.SSU2Util.*;
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@@ -80,20 +82,27 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
int len = pkt.getLength();
|
||||
byte data[] = pkt.getData();
|
||||
_rcvConnID = DataHelper.fromLong8(data, off);
|
||||
_sendConnID = DataHelper.fromLong8(data, off + 16);
|
||||
_sendConnID = DataHelper.fromLong8(data, off + SRC_CONN_ID_OFFSET);
|
||||
if (_rcvConnID == _sendConnID)
|
||||
throw new GeneralSecurityException("Identical Conn IDs");
|
||||
int type = data[off + 12] & 0xff;
|
||||
long token = DataHelper.fromLong8(data, off + 24);
|
||||
if (type == 10) {
|
||||
int type = data[off + TYPE_OFFSET] & 0xff;
|
||||
long token = DataHelper.fromLong8(data, off + TOKEN_OFFSET);
|
||||
if (type == TOKEN_REQUEST_FLAG_BYTE) {
|
||||
_currentState = InboundState.IB_STATE_TOKEN_REQUEST_RECEIVED;
|
||||
// TODO decrypt chacha?
|
||||
// decrypt in-place
|
||||
ChaChaPolyCipherState chacha = new ChaChaPolyCipherState();
|
||||
chacha.initializeKey(_rcvHeaderEncryptKey1, 0);
|
||||
long n = DataHelper.fromLong(data, off + PKT_NUM_OFFSET, 4);
|
||||
chacha.setNonce(n);
|
||||
chacha.decryptWithAd(data, off, LONG_HEADER_SIZE,
|
||||
data, off + LONG_HEADER_SIZE, data, off + LONG_HEADER_SIZE, len - LONG_HEADER_SIZE);
|
||||
processPayload(data, off + LONG_HEADER_SIZE, len - (LONG_HEADER_SIZE + MAC_LEN), true);
|
||||
_sendHeaderEncryptKey2 = introKey;
|
||||
do {
|
||||
token = ctx.random().nextLong();
|
||||
} while (token == 0);
|
||||
_token = token;
|
||||
} else if (type == 0 &&
|
||||
} else if (type == SESSION_REQUEST_FLAG_BYTE &&
|
||||
(token == 0 ||
|
||||
(ENFORCE_TOKEN && !_transport.getEstablisher().isInboundTokenValid(_remoteHostId, token)))) {
|
||||
_currentState = InboundState.IB_STATE_REQUEST_BAD_TOKEN_RECEIVED;
|
||||
@@ -104,20 +113,21 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
_token = token;
|
||||
} else {
|
||||
// fast MSB check for key < 2^255
|
||||
if ((data[off + 32 + 32 - 1] & 0x80) != 0)
|
||||
if ((data[off + LONG_HEADER_SIZE + KEY_LEN - 1] & 0x80) != 0)
|
||||
throw new GeneralSecurityException("Bad PK msg 1");
|
||||
// probably don't need again
|
||||
_token = token;
|
||||
_handshakeState.start();
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("State after start: " + _handshakeState);
|
||||
_handshakeState.mixHash(data, off, 32);
|
||||
_handshakeState.mixHash(data, off, LONG_HEADER_SIZE);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("State after mixHash 1: " + _handshakeState);
|
||||
|
||||
byte[] payload = new byte[len - 80]; // 32 hdr, 32 eph. key, 16 MAC
|
||||
// decrypt in-place
|
||||
try {
|
||||
_handshakeState.readMessage(data, off + 32, len - 32, payload, 0);
|
||||
_handshakeState.readMessage(data, off + LONG_HEADER_SIZE, len - LONG_HEADER_SIZE, data, off + LONG_HEADER_SIZE);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Session request error, State at failure: " + _handshakeState + '\n' + net.i2p.util.HexDump.dump(data, off, len), gse);
|
||||
@@ -125,7 +135,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
}
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("State after sess req: " + _handshakeState);
|
||||
processPayload(payload, payload.length, true);
|
||||
processPayload(data, off + LONG_HEADER_SIZE, len - (LONG_HEADER_SIZE + KEY_LEN + MAC_LEN), true);
|
||||
_sendHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader");
|
||||
_currentState = InboundState.IB_STATE_REQUEST_RECEIVED;
|
||||
}
|
||||
@@ -135,12 +145,12 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
@Override
|
||||
public int getVersion() { return 2; }
|
||||
|
||||
private void processPayload(byte[] payload, int length, boolean isHandshake) throws GeneralSecurityException {
|
||||
private void processPayload(byte[] payload, int offset, int length, boolean isHandshake) throws GeneralSecurityException {
|
||||
try {
|
||||
int blocks = SSU2Payload.processPayload(_context, this, payload, 0, length, isHandshake);
|
||||
int blocks = SSU2Payload.processPayload(_context, this, payload, offset, length, isHandshake);
|
||||
System.out.println("Processed " + blocks + " blocks");
|
||||
} catch (Exception e) {
|
||||
_log.error("IES2 payload error\n" + net.i2p.util.HexDump.dump(payload, 0, length));
|
||||
_log.error("IES2 payload error\n" + net.i2p.util.HexDump.dump(payload, 0, length), e);
|
||||
throw new GeneralSecurityException("IES2 payload error", e);
|
||||
}
|
||||
}
|
||||
@@ -363,9 +373,9 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("State after mixHash 1: " + _handshakeState);
|
||||
|
||||
byte[] payload = new byte[len - 80]; // 16 hdr, 32 static key, 16 MAC, 16 MAC
|
||||
// decrypt in-place
|
||||
try {
|
||||
_handshakeState.readMessage(data, off + 32, len - 32, payload, 0);
|
||||
_handshakeState.readMessage(data, off + LONG_HEADER_SIZE, len - LONG_HEADER_SIZE, data, off + LONG_HEADER_SIZE);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Session Request error, State at failure: " + _handshakeState + '\n' + net.i2p.util.HexDump.dump(data, off, len), gse);
|
||||
@@ -373,7 +383,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
}
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("State after sess req: " + _handshakeState);
|
||||
processPayload(payload, payload.length, true);
|
||||
processPayload(data, off + LONG_HEADER_SIZE, len - (SHORT_HEADER_SIZE + KEY_LEN + MAC_LEN + MAC_LEN), true);
|
||||
_sendHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader");
|
||||
_currentState = InboundState.IB_STATE_REQUEST_RECEIVED;
|
||||
|
||||
@@ -408,7 +418,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
|
||||
byte[] payload = new byte[len - 80]; // 16 hdr, 32 static key, 16 MAC, 16 MAC
|
||||
try {
|
||||
_handshakeState.readMessage(data, off + 16, len - 16, payload, 0);
|
||||
_handshakeState.readMessage(data, off + SHORT_HEADER_SIZE, len - SHORT_HEADER_SIZE, payload, 0);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Session Confirmed error, State at failure: " + _handshakeState + '\n' + net.i2p.util.HexDump.dump(data, off, len), gse);
|
||||
@@ -416,7 +426,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
}
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("State after sess conf: " + _handshakeState);
|
||||
processPayload(payload, payload.length, false);
|
||||
processPayload(payload, 0, payload.length, false);
|
||||
_sessCrForReTX = null;
|
||||
|
||||
// TODO split, calculate keys
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import com.southernstorm.noise.protocol.ChaChaPolyCipherState;
|
||||
import com.southernstorm.noise.protocol.CipherState;
|
||||
import com.southernstorm.noise.protocol.CipherStatePair;
|
||||
import com.southernstorm.noise.protocol.HandshakeState;
|
||||
@@ -20,6 +21,7 @@ import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterIdentity;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.RouterContext;
|
||||
import static net.i2p.router.transport.udp.SSU2Util.*;
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@@ -156,9 +158,9 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
_rcvHeaderEncryptKey2 = null;
|
||||
}
|
||||
|
||||
private void processPayload(byte[] payload, int length, boolean isHandshake) throws GeneralSecurityException {
|
||||
private void processPayload(byte[] payload, int offset, int length, boolean isHandshake) throws GeneralSecurityException {
|
||||
try {
|
||||
int blocks = SSU2Payload.processPayload(_context, this, payload, 0, length, isHandshake);
|
||||
int blocks = SSU2Payload.processPayload(_context, this, payload, offset, length, isHandshake);
|
||||
System.out.println("Processed " + blocks + " blocks");
|
||||
} catch (Exception e) {
|
||||
throw new GeneralSecurityException("Session Created payload error", e);
|
||||
@@ -264,6 +266,24 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
|
||||
public synchronized void receiveRetry(UDPPacket packet) throws GeneralSecurityException {
|
||||
////// TODO state check
|
||||
DatagramPacket pkt = packet.getPacket();
|
||||
int off = pkt.getOffset();
|
||||
int len = pkt.getLength();
|
||||
byte data[] = pkt.getData();
|
||||
try {
|
||||
// decrypt in-place
|
||||
ChaChaPolyCipherState chacha = new ChaChaPolyCipherState();
|
||||
chacha.initializeKey(_rcvHeaderEncryptKey1, 0);
|
||||
long n = DataHelper.fromLong(data, off + PKT_NUM_OFFSET, 4);
|
||||
chacha.setNonce(n);
|
||||
chacha.decryptWithAd(data, off, LONG_HEADER_SIZE,
|
||||
data, off + LONG_HEADER_SIZE, data, off + LONG_HEADER_SIZE, len - LONG_HEADER_SIZE);
|
||||
processPayload(data, off + LONG_HEADER_SIZE, len - (LONG_HEADER_SIZE + MAC_LEN), true);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Retry error", gse);
|
||||
throw gse;
|
||||
}
|
||||
createNewState(_routerAddress);
|
||||
////// TODO state change
|
||||
}
|
||||
@@ -283,13 +303,13 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
int off = pkt.getOffset();
|
||||
int len = pkt.getLength();
|
||||
byte data[] = pkt.getData();
|
||||
_handshakeState.mixHash(data, off, 32);
|
||||
_handshakeState.mixHash(data, off, LONG_HEADER_SIZE);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("State after mixHash 2: " + _handshakeState);
|
||||
|
||||
byte[] payload = new byte[len - 80]; // 32 hdr, 32 eph. key, 16 MAC
|
||||
// decrypt in-place
|
||||
try {
|
||||
_handshakeState.readMessage(data, off + 32, len - 32, payload, 0);
|
||||
_handshakeState.readMessage(data, off + LONG_HEADER_SIZE, len - LONG_HEADER_SIZE, data, off + LONG_HEADER_SIZE);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Session create error, State at failure: " + _handshakeState + '\n' + net.i2p.util.HexDump.dump(data, off, len), gse);
|
||||
@@ -297,7 +317,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
}
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("State after sess cr: " + _handshakeState);
|
||||
processPayload(payload, payload.length, true);
|
||||
processPayload(data, off + LONG_HEADER_SIZE, len - (LONG_HEADER_SIZE + KEY_LEN + MAC_LEN), true);
|
||||
_sessReqForReTX = null;
|
||||
_sendHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessionConfirmed");
|
||||
|
||||
|
||||
@@ -839,7 +839,7 @@ class PacketBuilder2 {
|
||||
byte data[] = pkt.getData();
|
||||
int off = pkt.getOffset();
|
||||
try {
|
||||
List<Block> blocks = new ArrayList<Block>(4);
|
||||
List<Block> blocks = new ArrayList<Block>(3);
|
||||
Block block = new SSU2Payload.DateTimeBlock(_context);
|
||||
int len = block.getTotalLength();
|
||||
blocks.add(block);
|
||||
@@ -850,8 +850,7 @@ class PacketBuilder2 {
|
||||
block = getPadding(len, 1280);
|
||||
len += block.getTotalLength();
|
||||
blocks.add(block);
|
||||
byte[] payload = new byte[len];
|
||||
SSU2Payload.writePayload(payload, 0, blocks);
|
||||
SSU2Payload.writePayload(data, off + LONG_HEADER_SIZE, blocks);
|
||||
|
||||
ChaChaPolyCipherState chacha = new ChaChaPolyCipherState();
|
||||
chacha.initializeKey(chachaKey, 0);
|
||||
@@ -881,7 +880,7 @@ class PacketBuilder2 {
|
||||
byte data[] = pkt.getData();
|
||||
int off = pkt.getOffset();
|
||||
try {
|
||||
List<Block> blocks = new ArrayList<Block>(4);
|
||||
List<Block> blocks = new ArrayList<Block>(2);
|
||||
Block block = new SSU2Payload.DateTimeBlock(_context);
|
||||
int len = block.getTotalLength();
|
||||
blocks.add(block);
|
||||
@@ -889,14 +888,13 @@ class PacketBuilder2 {
|
||||
block = getPadding(len, 1280);
|
||||
len += block.getTotalLength();
|
||||
blocks.add(block);
|
||||
byte[] payload = new byte[len];
|
||||
SSU2Payload.writePayload(payload, 0, blocks);
|
||||
SSU2Payload.writePayload(data, off + LONG_HEADER_SIZE, blocks);
|
||||
|
||||
ChaChaPolyCipherState chacha = new ChaChaPolyCipherState();
|
||||
chacha.initializeKey(chachaKey, 0);
|
||||
chacha.setNonce(n);
|
||||
chacha.encryptWithAd(data, off, LONG_HEADER_SIZE,
|
||||
data, off + LONG_HEADER_SIZE, data, off + LONG_HEADER_SIZE, len - LONG_HEADER_SIZE);
|
||||
data, off + LONG_HEADER_SIZE, data, off + LONG_HEADER_SIZE, len);
|
||||
|
||||
pkt.setLength(pkt.getLength() + len + MAC_LEN);
|
||||
} catch (RuntimeException re) {
|
||||
|
||||
Reference in New Issue
Block a user