wip accessing onion address
This commit is contained in:
parent
a5230b9105
commit
40ffd49b89
27
package-lock.json
generated
27
package-lock.json
generated
|
@ -2,6 +2,14 @@
|
|||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"base32": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/base32/-/base32-0.0.6.tgz",
|
||||
"integrity": "sha1-eQOLy1rsLY8ivMHChAKST1Cm0qw=",
|
||||
"requires": {
|
||||
"optimist": ">=0.1.0"
|
||||
}
|
||||
},
|
||||
"curve25519-js": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/curve25519-js/-/curve25519-js-0.0.4.tgz",
|
||||
|
@ -12,10 +20,29 @@
|
|||
"resolved": "https://registry.npmjs.org/futoin-hkdf/-/futoin-hkdf-1.3.2.tgz",
|
||||
"integrity": "sha512-3EVi3ETTyJg5PSXlxLCaUVVn0pSbDf62L3Gwxne7Uq+d8adOSNWQAad4gg7WToHkcgnCJb3Wlb1P8r4Evj4GPw=="
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
|
||||
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
|
||||
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
|
||||
"requires": {
|
||||
"minimist": "~0.0.1",
|
||||
"wordwrap": "~0.0.2"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
||||
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
|
||||
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"base32": "0.0.6",
|
||||
"curve25519-js": "0.0.4",
|
||||
"futoin-hkdf": "^1.3.2",
|
||||
"tweetnacl": "^1.0.3"
|
||||
|
|
26
src/index.js
26
src/index.js
|
@ -1,8 +1,7 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const url = require("url");
|
||||
const http = require("http");
|
||||
const https = require("https");
|
||||
const util = require("util");
|
||||
const Consensus = require("./tor/Consensus");
|
||||
const OR = require("./tor/OnionRouter");
|
||||
const Socket = require("./tor/Socket");
|
||||
|
@ -24,19 +23,23 @@ function timestamp() {
|
|||
const logger = {
|
||||
debug(...args) {
|
||||
if (LogLevels.debug > logLevel) return;
|
||||
console.debug(timestamp(), "D", ...args);
|
||||
const [format, ...rest] = args;
|
||||
console.debug(timestamp(), "D", util.format(format, ...rest));
|
||||
},
|
||||
info(...args) {
|
||||
if (LogLevels.info > logLevel) return;
|
||||
console.log(timestamp(), "I", ...args);
|
||||
const [format, ...rest] = args;
|
||||
console.debug(timestamp(), "I", util.format(format, ...rest));
|
||||
},
|
||||
warn(...args) {
|
||||
if (LogLevels.warn > logLevel) return;
|
||||
console.warn(timestamp(), "W", ...args);
|
||||
const [format, ...rest] = args;
|
||||
console.debug(timestamp(), "W", util.format(format, ...rest));
|
||||
},
|
||||
error(...args) {
|
||||
if (LogLevels.error > logLevel) return;
|
||||
console.error(timestamp(), "E", ...args);
|
||||
const [format, ...rest] = args;
|
||||
console.debug(timestamp(), "E", util.format(format, ...rest));
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -71,7 +74,8 @@ const logger = {
|
|||
/** @type Circuit */
|
||||
let circuit;
|
||||
try {
|
||||
const app_cache_path = path.join(process.env.HOME, '.cache', 'mini-tor-js');
|
||||
const home = process.env.HOME || process.env.USERPROFILE;
|
||||
const app_cache_path = path.join(home, '.cache', 'mini-tor-js');
|
||||
const cached_consensus_path = path.join(app_cache_path, 'cached-consensus')
|
||||
fs.mkdirSync(app_cache_path, { recursive: true });
|
||||
// Load consensus
|
||||
|
@ -131,7 +135,13 @@ const logger = {
|
|||
}
|
||||
}
|
||||
/** @type TorStream */
|
||||
const tor_stream = await circuit.create_stream(uri.hostname, uri.port);
|
||||
let tor_stream;
|
||||
if (uri.hostname.endsWith('.onion')) {
|
||||
const onion = uri.hostname.substr(0, uri.hostname.length - 6);
|
||||
tor_stream = await circuit.create_onion_stream(onion, uri.port);
|
||||
} else {
|
||||
tor_stream = await circuit.create_stream(uri.hostname, uri.port);
|
||||
}
|
||||
if (!tor_stream) {
|
||||
logger.error("Could not create stream to", uri.href);
|
||||
return;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
const HiddenServiceConnector = require('./HiddenServiceConnector');
|
||||
const CircuitNode = require('./CircuitNode');
|
||||
const Cell = require('./Cell')
|
||||
const RelayCell = require('./RelayCell')
|
||||
const Stream = require('./TorStream')
|
||||
const {sha1} = require('../utils/crypto')
|
||||
const { parseIp } = require('../utils/net')
|
||||
const { defer } = require('../utils/time')
|
||||
|
||||
|
@ -49,6 +51,10 @@ class Circuit {
|
|||
return this._node_list;
|
||||
}
|
||||
|
||||
get tor_socket() {
|
||||
return this._socket;
|
||||
}
|
||||
|
||||
get state() { return this._state };
|
||||
|
||||
set state(state) {
|
||||
|
@ -417,7 +423,20 @@ class Circuit {
|
|||
}
|
||||
}
|
||||
|
||||
extend(or, handshake_type) {
|
||||
async create_onion_stream(onion, port) {
|
||||
const hidden_service_connector = new HiddenServiceConnector(this, onion);
|
||||
|
||||
return await hidden_service_connector.connect()
|
||||
? this.create_stream(onion, port)
|
||||
: null;
|
||||
}
|
||||
|
||||
create_dir_stream() {
|
||||
//TODO
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
extend(or, handshake_type = 'ntor') {
|
||||
switch (handshake_type) {
|
||||
case 'ntor':
|
||||
return this._extend_ntor(or);
|
||||
|
@ -615,6 +634,132 @@ class Circuit {
|
|||
(this._waits[desired_state] = this._waits[desired_state] || []).push(d);
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
async rendezvous_establish(rendezvous_cookie) {
|
||||
//TODO
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
async rendezvous_introduce(rendezvous_circuit, rendezvous_cookie) {
|
||||
//mini_assert(rendezvous_cookie.get_size() == 20);
|
||||
|
||||
const introduction_point = this.get_final_circuit_node().onion_router;
|
||||
const introducee = rendezvous_circuit.get_final_circuit_node.onion_router;
|
||||
|
||||
this._logger.debug("circuit::rendezvous_introduce() [or: %s, state: introducing]", introduction_point.name);
|
||||
this.state = States.rendezvous_introducing;
|
||||
|
||||
this._logger.debug("circuit::rendezvous_introduce() [or: %s, state: completing]", introduction_point.name);
|
||||
rendezvous_circuit.state = States.rendezvous_completing;
|
||||
|
||||
//
|
||||
// payload of the RELAY_COMMAND_INTRODUCE1
|
||||
// command:
|
||||
//
|
||||
// PK_ID Identifier for Bob's PK [20 octets]
|
||||
// VER Version byte: set to 2. [1 octet]
|
||||
// IP Rendezvous point's address [4 octets]
|
||||
// PORT Rendezvous point's OR port [2 octets]
|
||||
// ID Rendezvous point identity ID [20 octets]
|
||||
// KLEN Length of onion key [2 octets]
|
||||
// KEY Rendezvous point onion key [KLEN octets]
|
||||
// RC Rendezvous cookie [20 octets]
|
||||
// g^x Diffie-Hellman data, part 1 [128 octets]
|
||||
//
|
||||
|
||||
//
|
||||
// compute PK_ID, aka hash of the service key.
|
||||
//
|
||||
const service_key_hash = sha1(introduction_point.service_key);
|
||||
|
||||
//
|
||||
// create rest of the payload in separate buffer;
|
||||
// it will be encrypted.
|
||||
//
|
||||
const handshake_bytes = Buffer.alloc(
|
||||
1 + // version
|
||||
4 + // ip address
|
||||
2 + // port
|
||||
20 + // identity_fingerprint
|
||||
2 + // onion key size
|
||||
32 + // onion key
|
||||
20 + // rendezvous cookie
|
||||
128); // DH
|
||||
|
||||
//io::memory_stream handshake_stream(handshake_bytes);
|
||||
//io::stream_wrapper handshake_buffer(handshake_stream, endianness::big_endian);
|
||||
|
||||
rendezvous_circuit._extend_node = new CircuitNode(this, introduction_point, circuit_node_type::introduction_point);
|
||||
|
||||
handshake_buffer.write(static_cast<uint8_t>(2));
|
||||
handshake_buffer.write(swap_endianness(introducee->get_ip_address().to_int()));
|
||||
handshake_buffer.write(introducee->get_or_port());
|
||||
handshake_buffer.write(introducee->get_identity_fingerprint());
|
||||
handshake_buffer.write(static_cast<payload_size_type>(introducee->get_onion_key().get_size()));
|
||||
handshake_buffer.write(introducee->get_onion_key());
|
||||
handshake_buffer.write(rendezvous_cookie);
|
||||
handshake_buffer.write(rendezvous_circuit->_extend_node->get_key_agreement().get_public_key());
|
||||
|
||||
const handshake_encrypted = hybrid_encryption::encrypt(
|
||||
handshake_bytes,
|
||||
introduction_point.service_key);
|
||||
|
||||
//
|
||||
// compose the final payload.
|
||||
//
|
||||
const relay_payload_bytes = Buffer.concat([
|
||||
service_key_hash,
|
||||
handshake_encrypted
|
||||
], service_key_hash.length + handshake_encrypted.length);
|
||||
|
||||
//
|
||||
// send the cell.
|
||||
//
|
||||
this.send_relay_cell(
|
||||
0,
|
||||
Cell.commands.relay_command_introduce1,
|
||||
relay_payload_bytes);
|
||||
|
||||
if (await this.wait_for_state(States.rendezvous_introduced))
|
||||
{
|
||||
this._logger.debug("circuit::rendezvous_introduce() [or: %s, state: introduced]", introduction_point.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._logger.error("circuit::rendezvous_introduce() [or: %s, is_rendezvous_introduced() == false]", introduction_point.name);
|
||||
|
||||
//
|
||||
// we cannot expect the rendezvous will be completed.
|
||||
//
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (await rendezvous_circuit.wait_for_state(States.rendezvous_completed))
|
||||
{
|
||||
this._logger.debug("circuit::rendezvous_introduce() [or: %s, state: completed]", introduction_point.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._logger.error("circuit::rendezvous_introduce() [or: %s, is_rendezvous_completed() == false]", introduction_point.name);
|
||||
}
|
||||
}
|
||||
|
||||
is_ready() {
|
||||
return this.state === States.ready;
|
||||
}
|
||||
|
||||
is_rendezvous_established() {
|
||||
return this.state === States.rendezvous_established;
|
||||
}
|
||||
|
||||
is_rendezvous_completed() {
|
||||
return this.state === States.rendezvous_completed;
|
||||
}
|
||||
|
||||
is_rendezvous_introduced() {
|
||||
return this.state === States.rendezvous_introduced;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Circuit;
|
||||
|
|
|
@ -18,7 +18,7 @@ const authorities = [
|
|||
authority_onion_router("moria1", "128.31.0.34", 9101, 9131),
|
||||
//authority_onion_router("tor26", "86.59.21.38", 443, 80},
|
||||
authority_onion_router("bastet", "204.13.164.118", 443, 80),
|
||||
authority_onion_router("maatuska", "171.25.193.9", 80, 443),
|
||||
//authority_onion_router("maatuska", "171.25.193.9", 80, 443),
|
||||
authority_onion_router("dannenberg", "193.23.244.244", 443, 80),
|
||||
authority_onion_router("Faravahar", "154.35.175.225", 443, 80),
|
||||
authority_onion_router("gabelmoo", "131.188.40.189", 443, 80),
|
||||
|
@ -197,7 +197,7 @@ class Consensus {
|
|||
port = router.dir_port;
|
||||
}
|
||||
|
||||
this._logger.debug(`consensus::download_from_random_authority() [path: http://${ip}:${port}${path}]`);
|
||||
this._logger.debug(`Consensus._download_from_random_router_impl() [path: http://${ip}:${port}${path}]`);
|
||||
|
||||
return get('http:', ip, port, path);
|
||||
}
|
||||
|
@ -236,6 +236,14 @@ class Consensus {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Buffer} identity_fingerprint
|
||||
* @returns {OnionRouter}
|
||||
*/
|
||||
get_onion_router_by_identity_fingerprint(identity_fingerprint) {
|
||||
return this._onion_router_map[identity_fingerprint.toString('hex')];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Consensus;
|
||||
|
|
346
src/tor/HiddenServiceConnector.js
Normal file
346
src/tor/HiddenServiceConnector.js
Normal file
|
@ -0,0 +1,346 @@
|
|||
const crypto = require('crypto');
|
||||
const base32 = require('base32')
|
||||
const OnionRouter = require('./OnionRouter')
|
||||
const {get} = require('../utils/http')
|
||||
const {time} = require('../utils/time')
|
||||
const {sha1} = require('../utils/crypto')
|
||||
|
||||
class HiddenServiceConnector {
|
||||
/** @type Consensus */
|
||||
_consensus = null;
|
||||
/** @type OnionRouter[] */
|
||||
_introduction_point_list = [];
|
||||
/** @type OnionRouter[] */
|
||||
_responsible_directory_list = [];
|
||||
/** @type Buffer */
|
||||
_rendezvous_cookie = null;
|
||||
|
||||
constructor (rendezvous_circuit, onion) {
|
||||
this._rendezvous_circuit = rendezvous_circuit;
|
||||
this._socket = rendezvous_circuit.tor_socket;
|
||||
this._consensus = rendezvous_circuit.tor_socket.onion_router.consensus;
|
||||
this._onion = onion;
|
||||
this._permanent_id = Buffer.from(base32.decode(onion));
|
||||
}
|
||||
|
||||
async connect() {
|
||||
this.find_responsible_directories();
|
||||
|
||||
if (this._responsible_directory_list.length)
|
||||
{
|
||||
//
|
||||
// create rendezvous cookie.
|
||||
//
|
||||
this._rendezvous_cookie = crypto.randomBytes(20);
|
||||
|
||||
//
|
||||
// establish rendezvous.
|
||||
//
|
||||
await this._rendezvous_circuit.rendezvous_establish(this._rendezvous_cookie);
|
||||
|
||||
if (this._rendezvous_circuit.is_rendezvous_established())
|
||||
{
|
||||
let responsible_directory_index = 0;
|
||||
while ((responsible_directory_index = await this.fetch_hidden_service_descriptor(responsible_directory_index)) !== -1)
|
||||
{
|
||||
await this.introduce();
|
||||
|
||||
if (this._rendezvous_circuit.is_rendezvous_completed())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
find_responsible_directories() {
|
||||
//
|
||||
// rend-spec.txt
|
||||
// 1.4.
|
||||
// At any time, there are 6 hidden service directories responsible for
|
||||
// keeping replicas of a descriptor; they consist of 2 sets of 3 hidden
|
||||
// service directories with consecutive onion IDs. Bob's OP learns about
|
||||
// the complete list of hidden service directories by filtering the
|
||||
// consensus status document received from the directory authorities. A
|
||||
// hidden service directory is deemed responsible for a descriptor ID if
|
||||
// it has the HSDir flag and its identity digest is one of the first three
|
||||
// identity digests of HSDir relays following the descriptor ID in a
|
||||
// circular list. A hidden service directory will only accept a descriptor
|
||||
// whose timestamp is no more than three days before or one day after the
|
||||
// current time according to the directory's clock.
|
||||
//
|
||||
|
||||
this._responsible_directory_list = [];
|
||||
|
||||
const directory_list = this._consensus.get_onion_routers_by_criteria({
|
||||
flags: OnionRouter.hsdir
|
||||
});
|
||||
|
||||
//
|
||||
// search for the 2 sets of 3 hidden service directories.
|
||||
//
|
||||
for (let replica = 0; replica < 2; replica++)
|
||||
{
|
||||
const descriptor_id = this.get_descriptor_id(replica);
|
||||
|
||||
const index = directory_list.findIndex(x => Buffer.compare(x.identity_fingerprint, descriptor_id) < 0);
|
||||
|
||||
for (let i = 0; i < 3; i++)
|
||||
{
|
||||
this._responsible_directory_list.push(directory_list[(index + i) % directory_list.length]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_secret_id(replica) {
|
||||
const permanent_id_byte = this._permanent_id[0];
|
||||
|
||||
//
|
||||
// rend-spec.txt
|
||||
// 1.3.
|
||||
//
|
||||
// "time-period" changes periodically as a function of time and
|
||||
// "permanent-id". The current value for "time-period" can be calculated
|
||||
// using the following formula:
|
||||
//
|
||||
// time-period = (current-time + permanent-id-byte * 86400 / 256)
|
||||
// / 86400
|
||||
//
|
||||
const time_period = (time() + (permanent_id_byte * 86400 / 256)) / 86400;
|
||||
|
||||
const secret_bytes = Buffer.alloc(5);
|
||||
secret_bytes.writeInt32BE(time_period);
|
||||
secret_bytes.writeUInt8(replica);
|
||||
|
||||
return sha1(secret_bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param replica
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
get_descriptor_id(replica) {
|
||||
const secret_id = this.get_secret_id(replica);
|
||||
|
||||
const descriptor_id_bytes = Buffer.concat([
|
||||
this._permanent_id,
|
||||
secret_id
|
||||
], this._permanent_id.length + secret_id.length);
|
||||
|
||||
return sha1(descriptor_id_bytes);
|
||||
}
|
||||
|
||||
async fetch_hidden_service_descriptor(responsible_directory_index) {
|
||||
for (let i = responsible_directory_index; i < this._responsible_directory_list.length; i++)
|
||||
{
|
||||
const responsible_directory = this._responsible_directory_list[i];
|
||||
|
||||
//
|
||||
// create new circuit and extend it with responsible directory.
|
||||
//
|
||||
this._logger.info(
|
||||
"\tCreating circuit for hidden service (try #%i), connecting to '%s' (%s:%i)",
|
||||
i + 1,
|
||||
this._socket.onion_router.name,
|
||||
this._socket.onion_router.ip,
|
||||
this._socket.onion_router.or_port);
|
||||
this._logger.info("\tConnected...");
|
||||
|
||||
/** @type Circuit */
|
||||
const directory_circuit = await this._socket.create_circuit();
|
||||
|
||||
if (!directory_circuit)
|
||||
{
|
||||
//
|
||||
// either tor socket is destroyed
|
||||
// or we couldn't create circuit with the first
|
||||
// onion router. try it again anyway.
|
||||
// but if the socket is destroyed, we're out of luck.
|
||||
//
|
||||
continue;
|
||||
}
|
||||
|
||||
this._logger.info(
|
||||
"\tExtending circuit for hidden service, connecting to responsible directory '%s' (%s:%u)",
|
||||
responsible_directory.name,
|
||||
responsible_directory.ip,
|
||||
responsible_directory.or_port);
|
||||
|
||||
await directory_circuit.extend(responsible_directory);
|
||||
|
||||
if (!directory_circuit.is_ready())
|
||||
{
|
||||
this._logger.warn("\tError while extending the directory circuit");
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// circuit must have exactly 2 nodes now.
|
||||
//
|
||||
//mini_assert(directory_circuit->get_circuit_node_list_size() == 2);
|
||||
|
||||
this._logger.info("\tExtended...");
|
||||
|
||||
const replica = i >= 3;
|
||||
|
||||
//
|
||||
// create the directory stream on the directory circuit.
|
||||
//
|
||||
const directory_stream = directory_circuit.create_dir_stream();
|
||||
|
||||
if (!directory_stream)
|
||||
{
|
||||
this._logger.warn("\tError while establishing the directory stream");
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// request the hidden service descriptor.
|
||||
//
|
||||
const descriptor_path = "/tor/rendezvous2/" + base32.encode(this.get_descriptor_id(replica));
|
||||
|
||||
this._logger.debug(
|
||||
"hidden_service::fetch_hidden_service_descriptor() [path: %s]",
|
||||
descriptor_path);
|
||||
|
||||
this._logger.info("\tSending request for hidden service descriptor...");
|
||||
|
||||
const hidden_service_descriptor = await get(
|
||||
'http:',
|
||||
responsible_directory.ip,
|
||||
responsible_directory.dir_port.toString(),
|
||||
descriptor_path,
|
||||
directory_stream);
|
||||
|
||||
this._logger.info("\tHidden service descriptor received...");
|
||||
|
||||
//
|
||||
// parse hidden service descriptor.
|
||||
//
|
||||
if (!hidden_service_descriptor &&
|
||||
!hidden_service_descriptor.includes("404 Not found"))
|
||||
{
|
||||
this._logger.info("\tHidden service descriptor is valid...");
|
||||
|
||||
let lines = hidden_service_descriptor.split('\n');
|
||||
let desc = '', capture = false;
|
||||
for (const line of lines) {
|
||||
if (line === '-----BEGIN MESSAGE-----') {
|
||||
capture = true;
|
||||
} else if (line === '-----END MESSAGE-----') {
|
||||
capture = false;
|
||||
break;
|
||||
} else if (capture) {
|
||||
desc += line;
|
||||
}
|
||||
}
|
||||
const introduction_point_list = [];
|
||||
const introduction_point_desc = Buffer.from(desc, 'base64').toString();
|
||||
lines = introduction_point_desc.split('\n');
|
||||
let current_router, serviceKey = null;
|
||||
for (const line of lines) {
|
||||
const parts = line.split(' ');
|
||||
if (parts[0] === 'introduction-point') {
|
||||
const identity_fingerprint = Buffer.from(base32.decode(parts[1]));
|
||||
current_router = this._consensus.get_onion_router_by_identity_fingerprint(identity_fingerprint);
|
||||
} else if (parts[0] === 'service-key') {
|
||||
serviceKey = '';
|
||||
} else if (parts[0] === '-----BEGIN RSA PUBLIC KEY-----' && serviceKey !== null) {
|
||||
capture = true;
|
||||
} else if (parts[0] === '-----END RSA PUBLIC KEY-----' && serviceKey !== null) {
|
||||
current_router.service_key = serviceKey;
|
||||
introduction_point_list.push(current_router);
|
||||
capture = false;
|
||||
serviceKey = null;
|
||||
} else if (capture) {
|
||||
serviceKey += line;
|
||||
}
|
||||
}
|
||||
|
||||
//mini_assert(!parser.introduction_point_list.is_empty());
|
||||
|
||||
if (introduction_point_list.length)
|
||||
{
|
||||
this._introduction_point_list = introduction_point_list
|
||||
}
|
||||
else
|
||||
{
|
||||
this._logger.warn("\tHidden service descriptor contains no introduction points...");
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._logger.warn("\tHidden service descriptor is invalid...");
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
async introduce() {
|
||||
for (const introduction_point of this._introduction_point_list)
|
||||
{
|
||||
this._logger.info(
|
||||
"\tCreating circuit for hidden service introduce, connecting to '%s' (%s:%i)",
|
||||
this._socket.onion_router.name,
|
||||
this._socket.onion_router.ip,
|
||||
this._socket.onion_router.or_port);
|
||||
|
||||
/** @type Circuit */
|
||||
const introduce_circuit = await this._socket.create_circuit();
|
||||
|
||||
if (!introduce_circuit)
|
||||
{
|
||||
//
|
||||
// either tor socket is destroyed
|
||||
// or we couldn't create circuit with the first
|
||||
// onion router. try it again anyway.
|
||||
// but if the socket is destroyed, we're out of luck.
|
||||
//
|
||||
continue;
|
||||
}
|
||||
|
||||
this._logger.info("\tConnected...");
|
||||
|
||||
this._logger.info(
|
||||
"\tExtending circuit to introduction point '%s' (%s:%u)",
|
||||
introduction_point.name,
|
||||
introduction_point.ip,
|
||||
introduction_point.or_port);
|
||||
|
||||
await introduce_circuit.extend(introduction_point);
|
||||
|
||||
if (!introduce_circuit.is_ready())
|
||||
{
|
||||
this._logger.warn("\tError while extending the introduce circuit");
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// circuit must have exactly 2 nodes now.
|
||||
//
|
||||
//mini_assert(introduce_circuit->get_circuit_node_list_size() == 2);
|
||||
|
||||
this._logger.info("\tExtended...");
|
||||
this._logger.info("\tSending introduce...");
|
||||
|
||||
await introduce_circuit.rendezvous_introduce(this._rendezvous_circuit, this._rendezvous_cookie);
|
||||
|
||||
if (introduce_circuit.is_rendezvous_introduced())
|
||||
{
|
||||
this._logger.info("\tIntroduced successfully...");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._logger.warn("\tIntroduce failed...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = HiddenServiceConnector;
|
|
@ -24,6 +24,7 @@ class OnionRouter {
|
|||
_identity_fingerprint = null;
|
||||
_descriptor_fetched = false;
|
||||
_ntor_onion_key = null;
|
||||
_service_key = null;
|
||||
|
||||
constructor (consensus, nickname, ip, or_port, dir_port, identity_fingerprint) {
|
||||
this._consensus = consensus;
|
||||
|
@ -34,17 +35,20 @@ class OnionRouter {
|
|||
this._identity_fingerprint = identity_fingerprint;
|
||||
}
|
||||
|
||||
get name() { return this._nickname };
|
||||
get ip() { return this._ip };
|
||||
get flags() { return this._flags };
|
||||
get or_port() { return this._or_port };
|
||||
get dir_port() { return this._dir_port };
|
||||
get consensus() { return this._consensus; }
|
||||
get name() { return this._nickname; }
|
||||
get ip() { return this._ip; }
|
||||
get flags() { return this._flags; }
|
||||
get or_port() { return this._or_port; }
|
||||
get dir_port() { return this._dir_port; }
|
||||
set flags(flags) { this._flags = flags; }
|
||||
get ntor_onion_key() {
|
||||
if (!this._descriptor_fetched) throw new Error('forgot to call fetch_descriptor for onion router');
|
||||
return this._ntor_onion_key;
|
||||
}
|
||||
get identity_fingerprint() { return this._identity_fingerprint; }
|
||||
set service_key(key) { this._service_key = key; }
|
||||
get service_key() { return this._service_key; }
|
||||
|
||||
async fetch_descriptor() {
|
||||
const descriptor = await this._consensus.get_onion_router_descriptor(this._identity_fingerprint);
|
||||
|
|
|
@ -34,6 +34,9 @@ class Socket {
|
|||
this._logger = logger;
|
||||
}
|
||||
|
||||
get onion_router() {
|
||||
return this._onion_router;
|
||||
}
|
||||
get state() {
|
||||
return this._state;
|
||||
}
|
||||
|
@ -90,6 +93,10 @@ class Socket {
|
|||
return this._socket && !this._socket.connecting;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param handshake
|
||||
* @returns {Promise<Circuit>}
|
||||
*/
|
||||
async create_circuit(handshake = 'ntor') {
|
||||
if (this.state !== States.ready)
|
||||
{
|
||||
|
|
|
@ -7,3 +7,13 @@ function hmac_sha256(key, data) {
|
|||
return h.update(data).digest();
|
||||
}
|
||||
exports.hmac_sha256 = hmac_sha256;
|
||||
|
||||
/**
|
||||
* @param {BinaryLike} data
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function sha1(data) {
|
||||
const h = crypto.createHash('sha1');
|
||||
return h.update(data).digest();
|
||||
}
|
||||
exports.sha1 = sha1;
|
||||
|
|
Loading…
Reference in New Issue
Block a user