Skip to content

Sifir-io/react-native-tor

Repository files navigation

React Native Tor

A fully featured Tor Daemon and Onion Routing Client for your React Native iOS and Android Project! :calling: :closed_lock_with_key: :globe_with_meridians:

TL;DR

In your project:

npm i react-native-tor
// for ios builds
npx pod-install

then

import Tor from "react-native-tor";

// Initialize the module
const tor = Tor();

//...

const makeTorRequest = async()=>{
    // Start the daemon and socks proxy (no need for Orbot and yes iOS supported!)
    await tor.startIfNotStarted();

    try{
       // Use built in client to make REST calls to .onion urls routed through the Sock5 proxy !
       const resp = await tor.get('http://some.onion',{'Authorization': 'sometoken'});
       // Note: self signed https endpoints, custom headers and multiform/json payloads supported!
       await tor.post('https://someother.onion',JSON.stringify(resp.json),{'Authorization': 'sometoken'},true);
    } catch(error){
      // Catch a network or server error like you normally with any other fetch library
    }
}

makeTorRequest();

đź’Ą

If you think this is awesome, please consider contributing to Privacy and Opensource !

Highlights

  • Embeds a fully functional Tor Daemon, with its own circuit (non exit) removing the dependency on Orbot and allowing Tor usage on IOS.
  • Provides a Socks5 proxy enabled REST client to allow you to make Rest calls on Onion URLs directly from JS just as you would with Axios, Frisbee etc..
  • Tcp socket support via a event like interface!
  • [WIP] Start a hidden service accessible via an Onion URL directly on your phone (in final test for upcoming 0.0.2 release)
  • Provides guard functions and state management options to autostart/stop the daemon when REST calls are initiated and/or the application is backgrounded/foregrounded
  • TS Typed API for sanity.

Disclaimer

*~~~ Beta software provided as is. I Accept no responsibility for use or misuse of Tor or this package. ~~~*


Why This ?

Privacy is important and should not be hard.

  • Users shouldn't be expected to install 3rd party apps and setup custom VPNS to get more privacy.
  • Devs shouldn't have to jump through hoops to get Onion urls routing correctly
  • React native is an awesome tool and this brings awesome privacy to it.

What This Does ?

Embeds Tor Daemon + Onion Routing into your App, removing the dependency on Orbot and allowing Tor usage on IOS

How it Does it ?

This module uses multiple layers:

  1. Pre-compiled rust sdk (sifir-rs-sdk) for the Tor daemon and Control code (Check CODE section for details)
  2. Swift/Kotlin wrappers to access and control the daemon and provide native proxified clients.
  3. TS/JS Native bridges to allow access from your components.

Requirements

  • ios > 11.1
  • Android minSdk > 26

Installation

  1. Install package
npm install react-native-tor

or

yarn add react-native-tor
  1. Link libs
  • Android: open android/app/build.gradle and add
    implementation files("../../node_modules/react-native-tor/android/libs/sifir_android.aar")
    
  • iOS:
    cd ios/
    pod install
    

iOS Projects note:

  • iOS 11.1 > only: Support iOS Version is 11.1 and higher (#6)
  • Bitcode not supported: Set Build Settings > Build Options > Enable Bitcode to No for both Debug and Release.

REST Usage Example

import Tor from "react-native-tor";

// Intialize the module with some settings
const tor = Tor({
  stopDaemonOnBackground :true, // Auto shut down daemon when app in background
});
// Start and boostrap the Tor daemon returning the socksProxy port that can be
// used to direct requests to.
const socksProxy = await tor.startIfNotStarted();


try{
    // Use module built in proxied client to make a get request
    const resp = tor.get('');
} catch(error){
    // Client throws on Network errors and Response codes > 299
}

Socket Usage Example

import Tor from 'react-native-tor';
const tor = Tor();
await tor.startIfNotStarted()
const target = 'kciybn4d4vuqvobdl2kdp3r2rudqbqvsymqwg4jomzft6m6gaibaf6yd.onion:50001';
const conn = await tor.createTcpConnection({ target }, (data, err) => {
  if(err){
    console.error('error sending msg',err);
    return
  }
  console.log('recieved tcp msg', data);
} );


try {
  await conn.write(`{ "id": 1, "method": "blockchain.scripthash.get_balance", "params": ["716decbe1660861c3d93906cb1d98ee68b154fd4d23aed9783859c1271b52a9c"] }\n`);
  await conn.write(`{ "id": 2, "method": "blockchain.scripthash.get_balance", "params": ["716decbe1660861c3d93906cb1d98ee68b154fd4d23aed9783859c1271b52a9c"] }\n`);
  // ... moar writes
} catch (err) {
  console.error('Error SendingTcpMSg', err);
}

// Eventually when you're done and *after* recieving your data close the connection
await conn.close();
}

Note:

  • The current TcpStream implementation emits per line of data received. That is it reads data from the socket until a new line is reached, at which time it will emit the string line received (by calling onData(data,null)
    • Ergo sum the current implementation is only suited for text data that is line delineated (Electrum server, etc..)
    • Future implementations will support constant streams of buffered base64 data.
  • If an error is received, or the connection is dropped onData will be called with the second parameter containing the error string (ie onData(null,'some error');
    • Receiving an 'EOF' error from the target we're connected to signifies the end of a stream or the target dropped the connection.
    • This will cause the module to drop the TcpConnection and remove all data event listeners.
    • Should you wish to reconnect to the target you must initiate a new connection by calling createTcpConnection again.

API reference

Please reference Typescript defs and JSDoc for details.

Documentation is a WIP, Contribute!

const tor = Tor({ ...params });

defined as :

/**
 * Tor module factory function
 * @param stopDaemonOnBackground
 * @default true
 * When set to true will shutdown the Tor daemon when the application is backgrounded preventing pre-emitive shutdowns by the OS
 * @param startDaemonOnActive
 * @default false
 * When set to true will automatically start/restart the Tor daemon when the application is bought back to the foreground (from the background)
 * @param os The OS the module is running on (Set automatically and is provided as an injectable for testing purposes)
 * @default The os the module is running on.
 */
declare const _default: ({ stopDaemonOnBackground, startDaemonOnActive, os, }?: {
    stopDaemonOnBackground?: boolean | undefined;
    startDaemonOnActive?: boolean | undefined;
    os?: "ios" | "android" | "windows" | "macos" | "web" | undefined;
}) => TorType;

This returns an instance of TorType module which gives control over the Tor daemon and access to the Socks5 enabled client:

declare type TorType = {
    /**
     * Send a GET request routed through the SOCKS proxy on the native side
     * Starts the Tor Daemon automatically if not already started
     * @param url
     * @param headers
     * @param trustSSL @default true When true accepts *any* SSL cert for the onion URL
     */
    get(url: string, headers?: RequestHeaders, trustSSL?: boolean): Promise<ProcessedRequestResponse>;
    /**
     * Send a POST request routed through the SOCKS proxy on the native side
     * Starts the Tor Daemon automatically if not already started
     * @param url
     * @param body JSON encoded (JSON.stringify) string of the payload to send * *Note*: Client will treat the body as a JSON string and set request header accordingly
     * The client also supports sending the payload as `application/x-www-form-urlencoded` by setting the `Content-Type` header to `application/x-www-form-urlencoded` in the header param
     * @param headers
     * @param trustSSL @default true When true accepts *any* SSL cert for the onion URL
     */
    post(url: string, body: RequestBody[RequestMethod.POST], headers?: RequestHeaders, trustSSL?: boolean): Promise<ProcessedRequestResponse>;
    /**
     * Send a DELETE request routed through the SOCKS proxy on the native side
     * Starts the Tor Daemon automatically if not already started
     * @param url
     * @param headers
     * @param trustSSL @default true When true accepts *any* SSL cert for the onion URL
     */
    delete(url: string, body?: RequestBody[RequestMethod.DELETE], headers?: RequestHeaders, trustSSL?: boolean): Promise<ProcessedRequestResponse>;
    /** Starts the Tor Daemon if not started and returns a promise that fullfills with the socks port number when boostraping is complete.
     * If the function was previously called it will return the promise without attempting to start the daemon again.
     * Useful when used as a guard in your transport or action layer
     */
    startIfNotStarted(): Promise<SocksPortNumber>;
    /**
     * Stops a running Tor Daemon
     */
    stopIfRunning(): Promise<void>;
    /**
     * Returns the current status of the Daemon
     * Some :
     * NOTINIT - Not initialized or run (call startIfNotStarted to the startDaemon)
     * STARTING - Daemon is starting and bootsraping
     * DONE - Daemon has completed boostraing and socks proxy is ready to be used to route traffic.
     * <other> - A status returned directly by the Daemon that can indicate a transient state or error.
     */
    getDaemonStatus(): Promise<string>;
    /**
     * Accessor the Native request function
     * Should not be used unless you know what you are doing.
     */
    request: NativeTor['request'];
    /**
     * Factory function for creating a peristant Tcp connection to a target
     * See createTcpConnectio;
     */
    createTcpConnection: typeof createTcpConnection;
};

Tcp Stream API:

interface TcpStream {
  /**
   * Called to close and end the Tcp connection
   */
  close(): Promise<boolean>;

  /**
   * Send a message (write on socket)
   * @param msg
   */
  write(msg: string): Promise<boolean>;
}

/**
 * /**
 * Factory function to create a persistent TcpStream connection to a target
 * Wraps the native side emitter and subscribes to the targets data messages (string).
 * The TcpStream currently emits per line of data received . That is it reads data from the socket until a new line is reached, at which time
 * it will emit the data read (by calling onData(data,null). If an error is received or the connection is dropped it onData will be called
 * with the second parameter containing the error string (ie onData(null,'some error');
 * Note: Receiving an 'EOF' error from the target we're connected to signifies the end of a stream or the target dropped the connection.
 *       This will cause the module to drop the TcpConnection and remove all data event listeners.
 *       Should you wish to reconnect to the target you must initiate a new connection by calling createTcpConnection again.
 * @param param {target: String, writeTimeout: Number} :
 *        `target` onion to connect to (ex: kciybn4d4vuqvobdl2kdp3r2rudqbqvsymqwg4jomzft6m6gaibaf6yd.onion:50001)
 *        'writeTimeout' in seconds to wait before timing out on writing to the socket (Defaults to 7)
 * @param onData TcpConnDatahandler node style callback called when data or an error is received for this connection
 * @returns TcpStream
 */
const createTcpConnection = async (
  param: { target: string; writeTimeout?: number },
  onData: TcpConnDatahandler
): Promise<TcpStream>

You can also check the provided Example Application for a quick reference.


Acknowledgments

  • Torproject for everything they do.
  • @afilini for his amazing work on libtor and constant support!
  • @GodSaysHodl for his generosity and backing of FOSS Bitcoin projects with financial bounties and encouragement.

Contributing to Privacy and Opensource

This module is the product of love and dedication for principles I believe in. If you find this module helpful or useful please do consider support it by contributing in the best way that suits you, here are some ideas:

Fund Privacy and Open Source !

Funding = More time to focus on what matters.

If you found this useful please help fund development for this and other privacy focused projects,

Coding

  • Check the TODO list below for some ideas on items flagged as valuable for users of Tor.
  • Check Development section below for instructions

See the contributing guide to learn how to contribute to the repository and the development workflow.

Development and Building from Scratch

Building for Native projects

There are 3 layers of libraries this project relies on:

  1. The Sifir Rust SDK providing lower level control of the Tor daemon and where building the iOS and Android libraries is done.
  2. The iOS and Android wrapper Code to bridge and provide a socks client. This is where the mobile native code interacts with the prebuilt Rust libraries with step1 via FFI and exposes that functionality to React. It is also where the proxied client forwards requests recieved from React.
  3. The Typescript module. This is the module React interacts with.

Depending on the level of modifications needed you will need to work with 1 or all 3 layers.

Building Android AAR and adding to Android Project

https://github.com/Sifir-io/sifir-rs-sdk/ Use the helper scripts/ for both the Android Tor and iOS Tor modules to compile them.

Adding compiled libs to this repo

  1. Copy the compiled Android AAR to the folder ./android/libs/
  2. Copy the compiled Universal iOS dylib to the folder ./ios/library/universal/ a. Modify Pod spec file if needed
  3. Bootstrap and run the example app to verify all is good
    1. yarn bootstrap
    2. cd example
    3. in one terminal yarn start
    4. in another terminal yarn ios or yarn android

Modify Native code

Modify Typescript Code

Share and Tell your friends

Know someone who want to add a bit more privacy to their Application / Product ? Help them out and tell them about this repo!

TODOs

WIP

  • Better API Docs
  • Event emitter from Rust to Native on Boostrap status
  • Enable secret service API
    • Start new hidden service on phone.
    • Restore hidden service from key.
  • Capture daemon logs into files.

Backlog

  • Search for available ports for socks proxy for iOS
  • Return a Context API (status, etc..) as part of the package to make it easier for developers to build reactive components on topof.
  • Build on Request capability
    • PUT calls
    • DELETE
      • Add body support
    • Sockets
    • Streaming ?
  • Investigate stability builds on older mobile API's (Currently minSdk is Android 26 and iOS 10)
  • Investigate the possibility of creating a NetworkExtension on iOS which act as a VPN for the app which regular REST libaries can be used on.

License

MIT