com.subgraph.orchid.circuits.NTorCircuitExtender Maven / Gradle / Ivy
package com.subgraph.orchid.circuits;
import java.util.logging.Logger;
import com.subgraph.orchid.CircuitNode;
import com.subgraph.orchid.RelayCell;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.TorException;
import com.subgraph.orchid.crypto.TorMessageDigest;
import com.subgraph.orchid.crypto.TorNTorKeyAgreement;
public class NTorCircuitExtender {
private final static Logger logger = Logger.getLogger(NTorCircuitExtender.class.getName());
private final CircuitExtender extender;
private final Router router;
private final TorNTorKeyAgreement kex;
public NTorCircuitExtender(CircuitExtender extender, Router router) {
this.extender = extender;
this.router = router;
this.kex = new TorNTorKeyAgreement(router.getIdentityHash(), router.getNTorOnionKey());
}
CircuitNode extendTo() {
final byte[] onion = kex.createOnionSkin();
if(finalRouterSupportsExtend2()) {
logger.fine("Extending circuit to "+ router.getNickname() + " with NTor inside RELAY_EXTEND2");
return extendWithExtend2(onion);
} else {
logger.fine("Extending circuit to "+ router.getNickname() + " with NTor inside RELAY_EXTEND");
return extendWithTunneledExtend(onion);
}
}
private CircuitNode extendWithExtend2(byte[] onion) {
final RelayCell cell = createExtend2Cell(onion);
extender.sendRelayCell(cell);
final RelayCell response = extender.receiveRelayResponse(RelayCell.RELAY_EXTENDED2, router);
return processExtended2(response);
}
private CircuitNode extendWithTunneledExtend(byte[] onion) {
final RelayCell cell = createExtendCell(onion, kex.getNtorCreateMagic());
extender.sendRelayCell(cell);
final RelayCell response = extender.receiveRelayResponse(RelayCell.RELAY_EXTENDED, router);
return processExtended(response);
}
private boolean finalRouterSupportsExtend2() {
return extender.getFinalRouter().getNTorOnionKey() != null;
}
private RelayCell createExtend2Cell(byte[] ntorOnionskin) {
final RelayCell cell = extender.createRelayCell(RelayCell.RELAY_EXTEND2);
cell.putByte(2);
cell.putByte(0);
cell.putByte(6);
cell.putByteArray(router.getAddress().getAddressDataBytes());
cell.putShort(router.getOnionPort());
cell.putByte(2);
cell.putByte(20);
cell.putByteArray(router.getIdentityHash().getRawBytes());
cell.putShort(0x0002);
cell.putShort(ntorOnionskin.length);
cell.putByteArray(ntorOnionskin);
return cell;
}
private RelayCell createExtendCell(byte[] ntorOnionskin, byte[] ntorMagic) {
final RelayCell cell = extender.createRelayCell(RelayCell.RELAY_EXTEND);
cell.putByteArray(router.getAddress().getAddressDataBytes());
cell.putShort(router.getOnionPort());
final int paddingLength = CircuitExtender.TAP_ONIONSKIN_LEN - (ntorOnionskin.length + ntorMagic.length);
final byte[] padding = new byte[paddingLength];
cell.putByteArray(ntorMagic);
cell.putByteArray(ntorOnionskin);
cell.putByteArray(padding);
cell.putByteArray(router.getIdentityHash().getRawBytes());
return cell;
}
private CircuitNode processExtended(RelayCell cell) {
byte[] payload = new byte[CircuitExtender.TAP_ONIONSKIN_REPLY_LEN];
cell.getByteArray(payload);
return processPayload(payload);
}
private CircuitNode processExtended2(RelayCell cell) {
final int payloadLength = cell.getShort();
if(payloadLength > cell.cellBytesRemaining()) {
throw new TorException("Incorrect payload length value in RELAY_EXTENED2 cell");
}
byte[] payload = new byte[payloadLength];
cell.getByteArray(payload);
return processPayload(payload);
}
private CircuitNode processPayload(byte[] payload) {
final byte[] keyMaterial = new byte[CircuitNodeCryptoState.KEY_MATERIAL_SIZE];
final byte[] verifyDigest = new byte[TorMessageDigest.TOR_DIGEST_SIZE];
if(!kex.deriveKeysFromHandshakeResponse(payload, keyMaterial, verifyDigest)) {
return null;
}
return extender.createNewNode(router, keyMaterial, verifyDigest);
}
}