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:
zzz
2022-02-28 16:24:59 -05:00
parent e53a59b4ac
commit 46ef49f2cf
3 changed files with 59 additions and 31 deletions

View File

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

View File

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

View File

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