com.subgraph.orchid.connections.ConnectionHandshake Maven / Gradle / Ivy
package com.subgraph.orchid.connections;
import java.io.IOException;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import javax.net.ssl.SSLSocket;
import com.subgraph.orchid.BridgeRouter;
import com.subgraph.orchid.Cell;
import com.subgraph.orchid.ConnectionHandshakeException;
import com.subgraph.orchid.ConnectionIOException;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.TorConfig;
import com.subgraph.orchid.circuits.cells.CellImpl;
import com.subgraph.orchid.crypto.TorPublicKey;
import com.subgraph.orchid.data.IPv4Address;
public abstract class ConnectionHandshake {
private final static Logger logger = Logger.getLogger(ConnectionHandshake.class.getName());
static ConnectionHandshake createHandshake(TorConfig config, ConnectionImpl connection, SSLSocket socket) throws ConnectionHandshakeException {
if(config.getHandshakeV3Enabled() && ConnectionHandshakeV3.sessionSupportsHandshake(socket.getSession())) {
return new ConnectionHandshakeV3(connection, socket);
} else if(config.getHandshakeV2Enabled()) {
return new ConnectionHandshakeV2(connection, socket);
} else {
throw new ConnectionHandshakeException("No valid handshake type available for this connection");
}
}
protected final ConnectionImpl connection;
protected final SSLSocket socket;
protected final List remoteVersions;
private int remoteTimestamp;
private IPv4Address myAddress;
private final List remoteAddresses;
ConnectionHandshake(ConnectionImpl connection, SSLSocket socket) {
this.connection = connection;
this.socket = socket;
this.remoteVersions = new ArrayList();
this.remoteAddresses = new ArrayList();
}
abstract void runHandshake() throws IOException, InterruptedException, ConnectionIOException;
int getRemoteTimestamp() {
return remoteTimestamp;
}
IPv4Address getMyAddress() {
return myAddress;
}
protected Cell expectCell(Integer... expectedTypes) throws ConnectionHandshakeException {
try {
final Cell c = connection.readConnectionControlCell();
for(int t: expectedTypes) {
if(c.getCommand() == t) {
return c;
}
}
final List expected = Arrays.asList(expectedTypes);
throw new ConnectionHandshakeException("Expecting Cell command "+ expected + " and got [ "+ c.getCommand() +" ] instead");
} catch (ConnectionIOException e) {
throw new ConnectionHandshakeException("Connection exception while performing handshake "+ e);
}
}
protected void sendVersions(int... versions) throws ConnectionIOException {
final Cell cell = CellImpl.createVarCell(0, Cell.VERSIONS, versions.length * 2);
for(int v: versions) {
cell.putShort(v);
}
connection.sendCell(cell);
}
protected void receiveVersions() throws ConnectionHandshakeException {
final Cell c = expectCell(Cell.VERSIONS);
while(c.cellBytesRemaining() >= 2) {
remoteVersions.add(c.getShort());
}
}
protected void sendNetinfo() throws ConnectionIOException {
final Cell cell = CellImpl.createCell(0, Cell.NETINFO);
putTimestamp(cell);
putIPv4Address(cell, connection.getRouter().getAddress());
putMyAddresses(cell);
connection.sendCell(cell);
}
private void putTimestamp(Cell cell) {
final Date now = new Date();
cell.putInt((int) (now.getTime() / 1000));
}
private void putIPv4Address(Cell cell, IPv4Address address) {
final byte[] data = address.getAddressDataBytes();
cell.putByte(Cell.ADDRESS_TYPE_IPV4);
cell.putByte(data.length);
cell.putByteArray(data);
}
private void putMyAddresses(Cell cell) {
cell.putByte(1);
putIPv4Address(cell, new IPv4Address(0));
}
protected void recvNetinfo() throws ConnectionHandshakeException {
processNetInfo(expectCell(Cell.NETINFO));
}
protected void processNetInfo(Cell netinfoCell) {
remoteTimestamp = netinfoCell.getInt();
myAddress = readAddress(netinfoCell);
final int addressCount = netinfoCell.getByte();
for(int i = 0; i < addressCount; i++) {
IPv4Address addr = readAddress(netinfoCell);
if(addr != null) {
remoteAddresses.add(addr);
}
}
}
private IPv4Address readAddress(Cell cell) {
final int type = cell.getByte();
final int len = cell.getByte();
if(type == Cell.ADDRESS_TYPE_IPV4 && len == 4) {
return new IPv4Address(cell.getInt());
}
final byte[] buffer = new byte[len];
cell.getByteArray(buffer);
return null;
}
protected void verifyIdentityKey(PublicKey publicKey) throws ConnectionHandshakeException {
if(!(publicKey instanceof RSAPublicKey)) {
throw new ConnectionHandshakeException("Identity certificate public key is not an RSA key as expected");
}
final TorPublicKey identityKey = new TorPublicKey((RSAPublicKey)publicKey);
final Router router = connection.getRouter();
if((router instanceof BridgeRouter) && (router.getIdentityHash() == null)) {
logger.info("Setting Bridge fingerprint from connection handshake for "+ router);
((BridgeRouter) router).setIdentity(identityKey.getFingerprint());
} else if(!identityKey.getFingerprint().equals(router.getIdentityHash())) {
throw new ConnectionHandshakeException("Router identity does not match certificate key");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy