Skip to content

A gRPC server wrapper for the https://github.com/axelarnetwork/tofn library.

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

entropyxyz/tofnd

 
 

Repository files navigation

Tofnd: A gRPC threshold signature scheme daemon

Tofnd is a gRPC server written in Rust that wraps the tofn threshold cryptography library.

Setup

The gRPC protobuf file is a separate submodule. To fetch it, please be sure that the --recursive flag is enabled:

git clone [email protected]:axelarnetwork/tofnd.git --recursive

tofnd uses the hyperium/tonic Rust gRPC implementation, which requires:

  • Rust 1.56 or greater
    $ rustup update
    
  • rustfmt to tidy up the code it generates
    $ rustup component add rustfmt
    

tofnd depends on tofn, which needs the GNU Multiple Precision Arithmetic Library

  • MacOS: brew install gmp
  • Ubuntu: sudo apt install libgmp-dev

Running the server

# install tofnd at ./target/release/tofnd
$ cargo install --path . && cd ./target/release

# init tofnd
$ ./tofnd -m create

# IMPORTANT: store the content of ./.tofnd/export file at a safe, offline place, and then delete the file
$ rm ./.tofnd/export

# start tofnd daemon
$ ./tofnd

Terminate the server with ctrl+C.

Password

By default, tofnd prompts for a password from stdin immediately upon launch. This password is used to encrypt on-disk storage. It is the responsibility of the user to keep this password safe.

Users may automate password entry as they see fit. Some examples follow. These examples are not necessarily secure as written---it's the responsibility of the user to secure password entry.

# feed password from MacOS keyring
$ security find-generic-password -a $(whoami) -s "tofnd" -w | ./tofnd

# feed password from 1password-cli
$ op get item tofnd --fields password | ./tofnd

# feed password from Pass
$ pass show tofnd | ./tofnd

# feed password from environment variable `PASSWORD`
$ echo $PASSWORD | ./tofnd

# feed password from a file `password.txt`
$ cat ./password.txt | ./tofnd

Sophisticated users may explicitly opt out of password entry via the --no-password terminal argument (see below). In this case, on-disk storage is not secure---it is the responsibility of the user to take additional steps to secure on-disk storage.

Command line arguments

We use clap to manage command line arguments.

Users can specify:

  1. Tofnd's root folder. Use --directory or -d to specify a full or a relative path. If no argument is provided, then the environment variable TOFND_HOME is used. If no environment variable is set either, the default ./tofnd directory is used.
  2. The port number of the gRPC server (default is 50051).
  3. The option to run in unsafe mode. By default, this option is off, and safe primes are used for keygen. Use the --unsafe flag only for testing.
  4. mnemonic operations for their tofnd instance (default is Existing). For more information, see on mnemonic options, see Mnemonic.
  5. The option to run in unsafe mode. By default, this option is off, and safe primes are used for keygen. Attention: Use the --unsafe flag only for testing.
  6. By default, tofnd expects a password from the standard input. Users that don't want to use passwords can use the --no-password flag. Attention: Use --no-password only for testing .
A threshold signature scheme daemon

USAGE:
    tofnd [FLAGS] [OPTIONS]

FLAGS:
        --no-password    Skip providing a password. Disabled by default. **Important note** If --no-password is set, the
                         a default (and public) password is used to encrypt.
        --unsafe         Use unsafe primes. Deactivated by default. **Important note** This option should only be used
                         for testing.
    -h, --help           Prints help information
    -V, --version        Prints version information

OPTIONS:
    -a, --address <ip>              [default: 0.0.0.0]
    -d, --directory <directory>     [env: TOFND_HOME=]  [default: .tofnd]
    -m, --mnemonic <mnemonic>       [default: existing]  [possible values: existing, create, import, export]
    -p, --port <port>               [default: 50051]]

Docker

Setup

To setup a tofnd container, use the create mnemonic command:

docker-compose run -e MNEMONIC_CMD=create tofnd

This will initialize tofnd, and then exit.

Execution

To run a tofnd daemon inside a container, run:

docker-compose up

Storage

We use data containers to persist data across restarts. To clean up storage, remove all tofnd containers, and run

docker volume rm tofnd_tofnd

Testing

For testing purposes, docker-compose.test.yml is available, which is equivelent to ./tofnd --no-password --unsafe. To spin up a test tofnd container, run

docker-compose -f docker-compose.test.yml up

The auto command

In containerized environments the auto mnemonic command can be used. This command is implemented in entrypoint.sh and does the following:

  1. Try to use existing mnemonic. If successful then launch tofnd server.
  2. Try to import a mnemonic from file. If successful then launch tofnd server.
  3. Create a new mnemonic. The newly created mnemonic is automatically written to the file TOFND_HOME/export---rename this file to TOFND_HOME/import so as to unblock future executions of tofnd. Then launch tofnd server.

The rationale behind auto is that users can frictionlessly launch and restart their tofnd nodes without the need to execute multiple commands. auto is currently the default command only in docker-compose.test.yml, but users can edit the docker-compose.yml to use it at their own discretion.

Attention: auto leaves the mnemonic on plain text on disk. You should remove the TOFND_HOME/import file and store the mnemonic at a safe, offline place.

Mnemonic

Tofnd uses the tiny-bip39 crate to enable users manage mnemonic passphrases. Currently, each party can use only one passphrase.

Mnemonic is used to enable recovery of shares in case of unexpected loss. See more about recovery under the Recover section.

Mnemonic options

The command line API supports the following commands:

  • Existing Starts the gRPC daemon using an existing mnemonic; Fails if no mnemonic exist.

  • Create Creates a new mnemonic, inserts it in the kv-store, exports it to a file and exits; Fails if a mnemonic already exists.

  • Import Prompts user to give a new mnemonic from standard input, inserts it in the kv-store and exits; Fails if a mnemonic exists or if the provided string is not a valid bip39 mnemonic.

  • Export Writes the existing mnemonic to <tofnd_root>/.tofnd/export and exits; Succeeds when there is an existing mnemonic. Fails if no mnemonic is stored, or the export file already exists.

Zeroization

We use the zeroize crate to clear sensitive info for memory as a good procatie. The data we clean are related to the mnemonic:

  1. entropy
  2. passwords
  3. passphrases

Note that, tiny-bip39 also uses zeroize internally.

KV Store

To persist information between different gRPCs (i.e. keygen and sign), we use a key-value storage based on sled.

Tofnd uses two separate KV Stores:

  1. Share KV Store. Stores all user's shares when keygen protocol is completed, and uses them for sign protocol. Default path is ./kvstore/shares.
  2. Mnemonic KV Store. Stores the entropy of a mnemonic passphrase. This entropy is used to encrypt and decrypt users' sensitive info, i.e. the content of the Share KV Store. Default path is ./kvstore/mnemonic.

Security

Important note: Currently, the mnemonic KV Store is not encrypted. The mnemonic entropy is stored in clear text on disk. Our current security model assumes secure device access.

Multiple shares

Multiple shares are handled internally. That is, if a party has 3 shares, the tofnd binary spawns 3 protocol execution threads, and each thread invokes tofn functions independently.

When a message is received from the gRPC client, it is broadcasted to all shares. This is done in the broadcast module.

At the end of the protocol, the outputs of all N party's shares are aggregated and a single result is created and sent to the client. There are separate modules keygen result and sign result that handles the aggregation results for each protocol.

For tofn support on multiple shares, see here.

gRPCs

Tofnd currently supports the following gRPCs:

  1. keygen
  2. sign
  3. recover

Keygen and sign use bidirectional streaming and recover is unary.

Diagrams

See a generic protocol sequence diagram, here.

See keygen and sign diagrams of detailed message flow of each protocol. By opening the .svg files at a new tab (instead of previewing from github), hyperlinks will be available that will point you to the code block in which the underlying operations are implemented.

Keygen

The keygen gRPC executes the keygen protocol as implemented in tofn and described in GG20.

The initialization of keygen is actualized by the following message:

message KeygenInit {
    string new_key_uid;  // keygen's identifier        
    repeated string party_uids;
    repeated uint32 party_share_counts;
    int32 my_party_index;       
    int32 threshold;
}

Successful keygen

On success, the keygen protocol returns a SecretKeyShare struct defined by tofn

pub struct SecretKeyShare {
    group: GroupPublicInfo,
    share: ShareSecretInfo,
}

This struct includes:

  1. The information that is needed by the party in order to participate in subsequent sign protocols that are associated with the completed keygen.
  2. The public key of the current keygen.

Since multiple shares per party are supported, keygen's result may produce multiple SecretKeyShares. The collection of SecretKeyShares is stored in the Share KV Store as the value with the key_uid as key.

Each SecretKeyShare is then encrypted using the party's mnemonic, and the encrypted data is sent to the client as bytes, along with the public key. We send the encrypted SecretKeyShares to facilitate recovery in case of data loss.

The gRPC message of keygen's data is the following:

message KeygenOutput {
    bytes pub_key = 1;                       // pub_key
    repeated bytes share_recovery_infos = 2; // recovery info
}

Unsuccessful keygen

The tofn library supports fault detection. That is, if a party does not follow the protocol (e.g. by corrupting zero knowledge proofs, stalling messages etc), a fault detection mechanism is triggered, and the protocol ends prematurely with all honest parties composing a faulter list.

In this case, instead of the aforementioned result, keygen returns a Vec<Faulters>, which is sent over the gRPC stream before closing the connection.

File structure

Keygen is implemented in tofnd/src/gg20/keygen, which has the following file structure:

├── keygen
    ├── mod.rs
    ├── init.rs
    ├── execute.rs
    ├── result.rs
    └── types.rs
  • In mod.rs, the handlers of protocol initialization, execution and aggregation of results are called. Also, in case of multiple shares, multiple execution threads are spawned.
  • In init.rs, the verification and sanitization of the Keygen Init message is handled.
  • In execute.rs, the instantiation and execution of the protocol is actualized.
  • In result.rs, the results of all party shares are aggregated, validated and sent to the gRPC client.
  • In types.rs, useful structs that are needed in the rest of the modules are defined.

Sign

The sign gRPC executes the sign protocol as implemented in tofn and described in GG20.

The initialization of sign is actualized by the following message:

message SignInit {
    string key_uid;     // keygen's identifier
    repeated string party_uids;
    bytes message_to_sign;
}

Successful sign

On success, the keygen protocol returns a signature which is a Vec<u8>.

Since multiple shares per party are supported, sign's result may produce multiple signaturess which are the same across all shares. Only one copy of the signature is sent to the gRPC client.

Unsuccessful sign

Similarly to keygen, if faulty parties are detected during the execution of sign, the protocol is stopped and a Vec<Faulters> is returned to the client.

Trigger recovery

Sign is started with the special gRPC message SignInit.

message SignInit {
    string key_uid = 1;
    repeated string party_uids = 2;
    bytes message_to_sign = 3;
}

key_uid indicates the session identifier of an executed keygen. In order to be able to participate to sign, parties need to have their share info stored at the Share KV Store as value, under the key key_uid. If this data is not present at the machine of a party (i.e. no key_uid exists in Share KV Store), a need_recover gRPC message is sent to the client and the connection is then closed. In the need_recover message, the missing key_uid is included.

message NeedRecover {
    string session_id = 1;
}

The client then proceeds by triggering recover gRPC, and then starts the sign again for the recovered party. Other participants are not affected.

File structure

The keygen protocol is implemented in tofnd/src/gg20/sign, which, similar to keygen, has the following file structure:

├── sign
    ├── mod.rs
    ├── init.rs
    ├── execute.rs
    ├── result.rs
    └── types.rs
  • In mod.rs, the handlers of protocol initialization, execution and aggregation of results are called. Also, in case of multiple shares, multiple execution threads are spawned.
  • In init.rs, the verification and sanitization of Sign Init message is handled. If the absence of shares is discovered, the client sends a need_recover and stops.
  • In execute.rs, the instantiation and execution of the protocol is actualized.
  • In result.rs, the results of all party shares are aggregated, validated and sent to the gRPC client.
  • In types.rs, useful structs that are needed in the rest of the modules are defined.

Recover

As discussed in keygen and sign section, the recovery of lost keys and shares is supported. In case of sudden data loss, for example due to a hard disk crash, parties are able to recover their shares. This is possible because each party sends it's encrypted secret info to the client before storing it inside the Share KV Store.

When keygen is completed, the party's information is encryped and sent to the client. When the absence of party's information is detected during sign, Tofnd sends the need_recover message, indicating that recovery must be triggered.

Recovery is a unary gRPC. The client re-sends the KeygenInit message and the encrypted recovery info. This allows Tofnd to reconstruct the Share KV Store by decrypting the recovery info using the party's mnemonic.

message RecoverRequest {
    KeygenInit keygen_init = 1;
    repeated bytes share_recovery_infos = 2;
}

If recovery was successful, a success message is sent, other wise Tofnd sends a fail message.

message RecoverResponse {
    enum Response {
        success = 0;
        fail = 1;
    }
    Response response = 1;
}

Testing

Honest behaviours

Both unit tests and integration tests are provided:

$ cargo test

Malicious behaviours

Tofn supports faulty behaviours to test fault detection. These behaviours are only supported under the malicious feature. See more for Rust features here.

Tofnd incorporates the malicious feature. You can run malicious tests by:

$ cargo test --all-features

License

All crates licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

A gRPC server wrapper for the https://github.com/axelarnetwork/tofn library.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Rust 97.9%
  • Shell 1.2%
  • Other 0.9%