package com.subgraph.orchid.circuits.hs; import java.nio.ByteBuffer; import java.util.logging.Logger; import com.subgraph.orchid.Cell; import com.subgraph.orchid.Circuit; import com.subgraph.orchid.RelayCell; import com.subgraph.orchid.Router; import com.subgraph.orchid.crypto.HybridEncryption; import com.subgraph.orchid.crypto.TorPublicKey; public class IntroductionProcessor { private final static Logger logger = Logger.getLogger(IntroductionProcessor.class.getName()); private final static int INTRODUCTION_PROTOCOL_VERSION = 3; private final HiddenService hiddenService; private final Circuit introductionCircuit; private final IntroductionPoint introductionPoint; protected IntroductionProcessor(HiddenService hiddenService, Circuit introductionCircuit, IntroductionPoint introductionPoint) { this.hiddenService = hiddenService; this.introductionCircuit = introductionCircuit; this.introductionPoint = introductionPoint; } TorPublicKey getServiceKey() { return introductionPoint.getServiceKey(); } boolean sendIntroduce(TorPublicKey permanentKey, byte[] publicKeyBytes, byte[] rendezvousCookie, Router rendezvousRouter) { final RelayCell introduceCell = introductionCircuit.createRelayCell(RelayCell.RELAY_COMMAND_INTRODUCE1, 0, introductionCircuit.getFinalCircuitNode()); final byte[] payload = createIntroductionPayload(rendezvousRouter, publicKeyBytes, rendezvousCookie, permanentKey); final TorPublicKey serviceKey = introductionPoint.getServiceKey(); introduceCell.putByteArray(serviceKey.getFingerprint().getRawBytes()); introduceCell.putByteArray(payload); introductionCircuit.sendRelayCell(introduceCell); final RelayCell response = introductionCircuit.receiveRelayCell(); if(response == null) { logger.fine("Timeout waiting for response to INTRODUCE1 cell"); return false; } else if(response.getRelayCommand() != RelayCell.RELAY_COMMAND_INTRODUCE_ACK) { logger.info("Unexpected relay cell type received waiting for response to INTRODUCE1 cell: "+ response.getRelayCommand()); return false; } else if(response.cellBytesRemaining() == 0) { return true; } else { logger.info("INTRODUCE_ACK indicates that introduction was not forwarded: "+ response.getByte()); return false; } } void markCircuitForClose() { introductionCircuit.markForClose(); } private byte[] createIntroductionPayload(Router rendezvousRouter, byte[] publicKeyBytes, byte[] rendezvousCookie, TorPublicKey encryptionKey) { final ByteBuffer buffer = createIntroductionBuffer((int) (System.currentTimeMillis() / 1000), rendezvousRouter, rendezvousCookie, publicKeyBytes); return encryptIntroductionBuffer(buffer, encryptionKey); } private ByteBuffer createIntroductionBuffer(int timestamp, Router rr, byte[] cookie, byte[] dhPublic) { final ByteBuffer buffer = ByteBuffer.allocate(Cell.CELL_LEN); final byte[] rpAddress = rr.getAddress().getAddressDataBytes(); final short rpPort = (short) rr.getOnionPort(); final byte[] rpIdentity = rr.getIdentityHash().getRawBytes(); final byte[] rpOnionKey = rr.getOnionKey().getRawBytes(); buffer.put((byte) INTRODUCTION_PROTOCOL_VERSION); // VER Version byte: set to 3. [1 octet] addAuthentication(buffer); //buffer.put((byte) 0); // AUTHT The auth type that is used [1 octet] buffer.putInt(timestamp); // TS A timestamp [4 octets] buffer.put(rpAddress); // IP Rendezvous point's address [4 octets] buffer.putShort(rpPort); // PORT Rendezvous point's OR port [2 octets] buffer.put(rpIdentity); // ID Rendezvous point identity ID [20 octets] buffer.putShort((short) rpOnionKey.length); // KLEN Length of onion key [2 octets] buffer.put(rpOnionKey); // KEY Rendezvous point onion key [KLEN octets] buffer.put(cookie); // RC Rendezvous cookie [20 octets] buffer.put(dhPublic); // g^x Diffie-Hellman data, part 1 [128 octets] return buffer; } private void addAuthentication(ByteBuffer buffer) { HSDescriptorCookie cookie = hiddenService.getAuthenticationCookie(); if(cookie == null) { buffer.put((byte) 0); } else { buffer.put(cookie.getAuthTypeByte()); buffer.putShort((short) cookie.getValue().length); buffer.put(cookie.getValue()); } } private byte[] encryptIntroductionBuffer(ByteBuffer buffer, TorPublicKey key) { final int len = buffer.position(); final byte[] payload = new byte[len]; buffer.flip(); buffer.get(payload); final HybridEncryption enc = new HybridEncryption(); return enc.encrypt(payload, key); } }