package com.subgraph.orchid.circuits.hs;
import java.math.BigInteger;
import java.util.logging.Logger;
import com.subgraph.orchid.Cell;
import com.subgraph.orchid.HiddenServiceCircuit;
import com.subgraph.orchid.InternalCircuit;
import com.subgraph.orchid.RelayCell;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.circuits.CircuitNodeCryptoState;
import com.subgraph.orchid.circuits.CircuitNodeImpl;
import com.subgraph.orchid.crypto.TorMessageDigest;
import com.subgraph.orchid.crypto.TorRandom;
import com.subgraph.orchid.crypto.TorTapKeyAgreement;
import com.subgraph.orchid.data.HexDigest;
public class RendezvousProcessor {
private final static Logger logger = Logger.getLogger(RendezvousProcessor.class.getName());
private final static int RENDEZVOUS_COOKIE_LEN = 20;
private final static TorRandom random = new TorRandom();
private final InternalCircuit circuit;
private final byte[] cookie;
protected RendezvousProcessor(InternalCircuit circuit) {
this.circuit = circuit;
this.cookie = random.getBytes(RENDEZVOUS_COOKIE_LEN);
}
boolean establishRendezvous() {
final RelayCell cell = circuit.createRelayCell(RelayCell.RELAY_COMMAND_ESTABLISH_RENDEZVOUS, 0, circuit.getFinalCircuitNode());
cell.putByteArray(cookie);
circuit.sendRelayCell(cell);
final RelayCell response = circuit.receiveRelayCell();
if(response == null) {
logger.info("Timeout waiting for Rendezvous establish response");
return false;
} else if(response.getRelayCommand() != RelayCell.RELAY_COMMAND_RENDEZVOUS_ESTABLISHED) {
logger.info("Response received from Rendezvous establish was not expected acknowledgement, Relay Command: "+ response.getRelayCommand());
return false;
} else {
return true;
}
}
HiddenServiceCircuit processRendezvous2(TorTapKeyAgreement kex) {
final RelayCell cell = circuit.receiveRelayCell();
if(cell == null) {
logger.info("Timeout waiting for RENDEZVOUS2");
return null;
} else if (cell.getRelayCommand() != RelayCell.RELAY_COMMAND_RENDEZVOUS2) {
logger.info("Unexpected Relay cell type received while waiting for RENDEZVOUS2: "+ cell.getRelayCommand());
return null;
}
final BigInteger peerPublic = readPeerPublic(cell);
final HexDigest handshakeDigest = readHandshakeDigest(cell);
if(peerPublic == null || handshakeDigest == null) {
return null;
}
final byte[] verifyHash = new byte[TorMessageDigest.TOR_DIGEST_SIZE];
final byte[] keyMaterial = new byte[CircuitNodeCryptoState.KEY_MATERIAL_SIZE];
if(!kex.deriveKeysFromDHPublicAndHash(peerPublic, handshakeDigest.getRawBytes(), keyMaterial, verifyHash)) {
logger.info("Error deriving session keys while extending to hidden service");
return null;
}
return circuit.connectHiddenService(CircuitNodeImpl.createAnonymous(circuit.getFinalCircuitNode(), keyMaterial, verifyHash));
}
private BigInteger readPeerPublic(Cell cell) {
final byte[] dhPublic = new byte[TorTapKeyAgreement.DH_LEN];
cell.getByteArray(dhPublic);
final BigInteger peerPublic = new BigInteger(1, dhPublic);
if(!TorTapKeyAgreement.isValidPublicValue(peerPublic)) {
logger.warning("Illegal DH public value received: "+ peerPublic);
return null;
}
return peerPublic;
}
HexDigest readHandshakeDigest(Cell cell) {
final byte[] digestBytes = new byte[TorMessageDigest.TOR_DIGEST_SIZE];
cell.getByteArray(digestBytes);
return HexDigest.createFromDigestBytes(digestBytes);
}
byte[] getCookie() {
return cookie;
}
Router getRendezvousRouter() {
return circuit.getFinalCircuitNode().getRouter();
}
}