package org.torproject.jtor.circuits.impl;
import java.math.BigInteger;
import org.torproject.jtor.TorException;
import org.torproject.jtor.circuits.CircuitNode;
import org.torproject.jtor.circuits.cells.Cell;
import org.torproject.jtor.circuits.cells.RelayCell;
import org.torproject.jtor.crypto.HybridEncryption;
import org.torproject.jtor.crypto.TorKeyAgreement;
import org.torproject.jtor.crypto.TorMessageDigest;
import org.torproject.jtor.data.HexDigest;
import org.torproject.jtor.directory.Router;
public class CircuitNodeImpl implements CircuitNode {
static CircuitNodeImpl createForRouter(Router router) {
return new CircuitNodeImpl(router);
}
private final static int CIRCWINDOW_START = 1000;
private final static int CIRCWINDOW_INCREMENT = 100;
private final TorKeyAgreement dhContext;
private final Router router;
private CircuitNodeCryptoState cryptoState;
private final CircuitNodeImpl previousNode;
private final Object windowLock;
private int packageWindow;
private int deliverWindow;
private CircuitNodeImpl(Router router) {
this(router, null);
}
CircuitNodeImpl(Router router, CircuitNodeImpl previous) {
previousNode = previous;
this.router = router;
this.dhContext = new TorKeyAgreement();
windowLock = new Object();
packageWindow = CIRCWINDOW_START;
deliverWindow = CIRCWINDOW_START;
}
public TorKeyAgreement getContext(){
return dhContext;
}
public Router getRouter() {
return router;
}
void setSharedSecret(BigInteger peerPublic, HexDigest packetDigest) {
if(!TorKeyAgreement.isValidPublicValue(peerPublic))
throw new TorException("Illegal DH public value");
final byte[] sharedSecret = dhContext.getSharedSecret(peerPublic);
deriveKeys(sharedSecret);
if(!cryptoState.verifyPacketDigest(packetDigest))
throw new TorException("Digest verification failed!");
}
public CircuitNodeImpl getPreviousNode() {
return previousNode;
}
public void encryptForwardCell(RelayCell cell) {
cryptoState.encryptForwardCell(cell);
}
boolean decryptBackwardCell(Cell cell) {
return cryptoState.decryptBackwardCell(cell);
}
public void updateForwardDigest(RelayCell cell) {
cryptoState.updateForwardDigest(cell);
}
public byte[] getForwardDigestBytes() {
return cryptoState.getForwardDigestBytes();
}
byte[] createOnionSkin() {
final byte[] yBytes = dhContext.getPublicKeyBytes();
final HybridEncryption hybrid = new HybridEncryption();
return hybrid.encrypt(yBytes, router.getOnionKey());
}
private void deriveKeys(byte[] sharedSecret) {
byte[] keyMaterial = new byte[TorMessageDigest.TOR_DIGEST_SIZE * 5];
int offset = 0;
byte[] buffer = new byte[sharedSecret.length + 1];
System.arraycopy(sharedSecret, 0, buffer, 0, sharedSecret.length);
for(int i = 0; i < 5; i++) {
final TorMessageDigest md = new TorMessageDigest();
buffer[sharedSecret.length] = (byte)i;
md.update(buffer);
System.arraycopy(md.getDigestBytes(), 0, keyMaterial, offset, TorMessageDigest.TOR_DIGEST_SIZE);
offset += TorMessageDigest.TOR_DIGEST_SIZE;
}
cryptoState = CircuitNodeCryptoState.createFromKeyMaterial(keyMaterial);
}
public String toString() {
return "|"+ router.getNickname() + "|";
}
public void decrementDeliverWindow() {
synchronized(windowLock) {
deliverWindow--;
}
}
public boolean considerSendingSendme() {
synchronized(windowLock) {
if(deliverWindow <= (CIRCWINDOW_START - CIRCWINDOW_INCREMENT)) {
deliverWindow += CIRCWINDOW_INCREMENT;
return true;
}
return false;
}
}
public void waitForSendWindow() {
waitForSendWindow(false);
}
public void waitForSendWindowAndDecrement() {
waitForSendWindow(true);
}
private void waitForSendWindow(boolean decrement) {
synchronized(windowLock) {
while(packageWindow == 0) {
try {
windowLock.wait();
} catch (InterruptedException e) {
throw new TorException("Thread interrupted while waiting for circuit send window");
}
}
if(decrement)
packageWindow--;
}
}
public void incrementSendWindow() {
synchronized(windowLock) {
packageWindow += CIRCWINDOW_INCREMENT;
windowLock.notifyAll();
}
}
}