Skip to content

Commit

Permalink
chore: update deps (#331)
Browse files Browse the repository at this point in the history
* refactor!: remove gg20 support

* clean pin rust version and clean up actions

* remove safe prime config

* remove gg20 types

* update protobufs

* update submodule

* update dockerfile

* update edition

* change imports

* update clap

* update deps

* use ubuntu runner

* revert runner change

* cast from proto enum

* update manifest and readme
  • Loading branch information
milapsheth authored Jul 10, 2024
1 parent 17fbe76 commit 6eba099
Show file tree
Hide file tree
Showing 16 changed files with 836 additions and 1,374 deletions.
2,029 changes: 748 additions & 1,281 deletions Cargo.lock

Large diffs are not rendered by default.

64 changes: 33 additions & 31 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,59 +1,61 @@
[package]
name = "tofnd"
version = "0.11.0"
authors = ["Gus Gutoski <gus@axelar.network>", "Stelios Daveas <stelios@axelar.network>"]
edition = "2018"
version = "1.0.0"
authors = ["Interoplabs Eng <eng@interoplabs.io>"]
edition = "2021"
license = "MIT OR Apache-2.0"
description = "A cryptographic signing service, used by the Axelar network"
keywords = ["cryptography", "blockchain", "axelar", "ecdsa", "ed25519"]

[dependencies]
tonic = "0.6"
tofn = { version = "1.0" }
# tofn = { path = "../tofn" }
sled = {version = "0.34", default-features = false}

# logging
log = {version = "0.4",default-features = false }
tracing = {version = "0.1", default-features = false}
tracing-subscriber= {version = "0.3", features = ["json", "env-filter"]}
atty = {version = "0.2", default-features = false}
log = { version = "0.4",default-features = false }
tracing = { version = "0.1", default-features = false }
tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] }
atty = { version = "0.2", default-features = false }

# config
clap = {version = "3.0", default-features = false, features = ["std", "cargo", "env"]}
# CLI args
clap = { version = "3.2", default-features = false, features = ["std", "cargo", "env"] }

# sled dependency
# kv store
sled = { version = "0.34", default-features = false }
serde = { version = "1.0", features = ["derive"], default-features = false }
# sled encryption
chacha20poly1305 = { version = "0.9", features = ["alloc"], default-features = false }
rand = {version = "0.8", default-features = false }
dirs = { version = "5.0", default-features = false }

rpassword = { version = "5.0", default-features = false }
# kv store encryption
chacha20poly1305 = { version = "0.10", features = ["alloc"], default-features = false }
rand = { version = "0.8", default-features = false }
rpassword = { version = "7.3", default-features = false }
scrypt = { version = "0.11", default-features = false, features = ["std"] }

# tonic dependencies
prost = {version = "0.9", default-features = false}
tokio = { version = "1.8", features = ["rt-multi-thread", "macros", "signal", "net", "sync"], default-features = false }
tokio-stream = {version = "0.1.7", features = ["net"], default-features = false}
futures-util = {version = "0.3", default-features = false}
# gRPC server
tonic = { version = "0.6" } # ensure tonic-build version matches this
prost = { version = "0.9" }

# async runtime
tokio = { version = "1.38", features = ["rt-multi-thread", "macros", "signal", "net", "sync"], default-features = false }
tokio-stream = { version = "0.1.15", features = ["net"], default-features = false }
futures-util = { version = "0.3", default-features = false }

# mnemonic
tiny-bip39 = { version = "0.8.2", default-features = false}
zeroize = { version = "1.4", features = ["zeroize_derive"], default-features = false}
tiny-bip39 = { version = "1.0.0", default-features = false}
zeroize = { version = "1.8", features = ["zeroize_derive"], default-features = false}

#error handling
# error handling
thiserror = { version = "1.0", default-features = false }
anyhow = { version = "1.0", default-features = false }

dirs = { version = "4.0", default-features = false }

[build-dependencies]
tonic-build = {version = "0.6"}
tonic-build = { version = "0.6" }

[dev-dependencies]
lazy_static = { version = "1.4", default-features = false}
lazy_static = { version = "1.5", default-features = false}
# enable logging for tests
tracing-test = {version = "0.2", default-features = false}
tracing-test = { version = "0.2", default-features = false }

testdir = {version = "0.4", default-features = false}
testdir = { version = "0.9", default-features = false }

# Don't abort in case there is a panic to clean up data
[profile.dev]
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:experimental

FROM rust:1.73.0-bullseye as builder
FROM rust:1.78.0-bullseye as builder

RUN set -ex \
&& apt-get update \
Expand Down
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Tofnd: A gRPC threshold signature scheme daemon
# tofnd: A cryptographic signing service

Tofnd is a [gRPC](https://grpc.io/) server written in Rust that wraps the [tofn](https://github.com/axelarnetwork/tofn) cryptography library.

Expand Down Expand Up @@ -91,12 +91,13 @@ 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]]
-p, --port <port> [default: 50051]
```

## Docker

### Setup
### Docker Setup

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

```bash
Expand Down Expand Up @@ -164,7 +165,6 @@ We use the [zeroize](https://docs.rs/zeroize/1.1.1/zeroize/) crate to clear sens

1. entropy
2. passwords
3. passphrases

Note that, [tiny-bip39](https://docs.rs/crate/tiny-bip39) also uses `zeroize` internally.

Expand All @@ -174,10 +174,6 @@ To persist information between different gRPCs (i.e. _keygen_ and _sign_), we us

`Tofnd` uses an encrypted mnemonic KV Store which stores the entropy of a mnemonic passphrase. This entropy is used to derive user's keys. The KV Store is encrypted with a password provided by the user. The password is used to derive a key that encrypts the KV Store.

## 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.

## Threshold cryptography

For an implementation of the [GG20](https://eprint.iacr.org/2020/540.pdf) threshold-ECDSA protocol,
Expand Down
5 changes: 1 addition & 4 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Use [`compile_protos`] only if you don't need to tweak anything
// tonic_build::compile_protos("proto/tofnd.proto")?;

// client build needed only for tests https://github.com/rust-lang/cargo/issues/1581
// TODO: client build is needed only for tests https://github.com/rust-lang/cargo/issues/1581
tonic_build::configure()
// .build_client(false)
// .out_dir(".") // if you want to peek at the generated code
Expand Down
27 changes: 14 additions & 13 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::path::PathBuf;

use clap::{crate_version, App, Arg};
use clap::{builder::PossibleValuesParser, crate_version, Arg, Command};

// error handling
use crate::{encrypted_sled::PasswordMethod, mnemonic::Cmd, TofndResult};
Expand Down Expand Up @@ -40,7 +40,7 @@ pub fn parse_args() -> TofndResult<Config> {
.to_str()
.ok_or_else(|| anyhow!("can't convert default dir to str"))?;

let app = App::new("tofnd")
let app = Command::new("tofnd")
.about("A cryptographic signing service")
.version(crate_version!())
.arg(
Expand Down Expand Up @@ -73,7 +73,8 @@ pub fn parse_args() -> TofndResult<Config> {
.short('m')
.required(false)
.default_value(DEFAULT_MNEMONIC_CMD)
.possible_values(AVAILABLE_MNEMONIC_CMDS),
.value_parser(PossibleValuesParser::new(AVAILABLE_MNEMONIC_CMDS))
.takes_value(true),
)
.arg(
Arg::new("directory")
Expand All @@ -87,23 +88,23 @@ pub fn parse_args() -> TofndResult<Config> {
let matches = app.get_matches();

let ip = matches
.value_of("ip")
.get_one::<String>("ip")
.ok_or_else(|| anyhow!("ip value"))?
.to_string();
.clone();
let port = matches
.value_of("port")
.get_one::<String>("port")
.ok_or_else(|| anyhow!("port value"))?
.parse::<u16>()?;
let mnemonic_cmd = matches
.value_of("mnemonic")
.ok_or_else(|| anyhow!("cmd value"))?
.to_string();
let mnemonic_cmd = Cmd::from_string(&mnemonic_cmd)?;
let mnemonic_cmd = Cmd::from_string(
matches
.get_one::<String>("mnemonic")
.ok_or_else(|| anyhow!("cmd value"))?,
)?;
let tofnd_path = matches
.value_of("directory")
.get_one::<String>("directory")
.ok_or_else(|| anyhow!("directory value"))?
.into();
let password_method = match matches.is_present("no-password") {
let password_method = match matches.contains_id("no-password") {
true => PasswordMethod::NoPassword,
false => PasswordMethod::Prompt,
};
Expand Down
2 changes: 1 addition & 1 deletion src/encrypted_sled/kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use std::convert::TryInto;

use chacha20poly1305::aead::{AeadInPlace, NewAead};
use chacha20poly1305::aead::{AeadInPlace, KeyInit};
use chacha20poly1305::{self, XChaCha20Poly1305};
use rand::RngCore;

Expand Down
4 changes: 2 additions & 2 deletions src/encrypted_sled/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use testdir::testdir;

#[test]
fn test_encrypted_sled() {
let db_path = testdir!("encrypted_db");
let db_path = testdir!("encrypted_sled");
let db = EncryptedDb::open(db_path, get_test_password()).unwrap();

// insert <key: value> -> returns None
Expand Down Expand Up @@ -45,7 +45,7 @@ fn test_encrypted_sled() {

#[test]
fn test_use_existing_salt() {
let db_path = testdir!("encrypted_db");
let db_path = testdir!("use_existing_salt");
let db = EncryptedDb::open(&db_path, get_test_password()).unwrap();
drop(db);
// open existing db
Expand Down
8 changes: 4 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use multisig::service::MultisigService;
use proto::multisig_server::MultisigServer;
use std::net::SocketAddr;
use tokio::net::TcpListener;
use tokio_stream::wrappers::TcpListenerStream;
Expand Down Expand Up @@ -56,14 +58,12 @@ async fn main() -> TofndResult<()> {
.handle_mnemonic(&cfg.mnemonic_cmd)
.await?;

let multisig_service = multisig::service::new_service(kv_manager);

if cmd.exit_after_cmd() {
info!("Tofnd exited after using command <{:?}>. Run `./tofnd -m existing` to execute gRPC daemon.", cmd);
return Ok(());
}

let multisig_service = proto::multisig_server::MultisigServer::new(multisig_service);
let service = MultisigServer::new(MultisigService::new(kv_manager));

let incoming = TcpListener::bind(socket_address).await?;
info!(
Expand All @@ -72,7 +72,7 @@ async fn main() -> TofndResult<()> {
);

tonic::transport::Server::builder()
.add_service(multisig_service)
.add_service(service)
.serve_with_incoming_shutdown(TcpListenerStream::new(incoming), shutdown_signal())
.await?;

Expand Down
4 changes: 2 additions & 2 deletions src/multisig/key_presence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ impl MultisigService {
&self,
request: proto::KeyPresenceRequest,
) -> TofndResult<proto::key_presence_response::Response> {
// check if mnemonic is available
let algorithm = Algorithm::from_i32(request.algorithm)
.ok_or(anyhow!("Invalid algorithm: {}", request.algorithm))?;
.ok_or_else(|| anyhow!("Invalid algorithm: {}", request.algorithm))?;

// check if mnemonic is available
let _ = self
.find_matching_seed(&request.key_uid, &request.pub_key, algorithm)
.await?;
Expand Down
4 changes: 2 additions & 2 deletions src/multisig/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ use anyhow::anyhow;
impl MultisigService {
pub(super) async fn handle_keygen(&self, request: &KeygenRequest) -> TofndResult<Vec<u8>> {
let algorithm = Algorithm::from_i32(request.algorithm)
.ok_or(anyhow!("Invalid algorithm: {}", request.algorithm))?;
.ok_or_else(|| anyhow!("Invalid algorithm: {}", request.algorithm))?;
let secret_recovery_key = self.kv_manager.seed().await?;

Ok(
KeyPair::generate(&secret_recovery_key, request.key_uid.as_bytes(), algorithm)?
KeyPair::new(&secret_recovery_key, request.key_uid.as_bytes(), algorithm)?
.encoded_verifying_key(),
)
}
Expand Down
15 changes: 7 additions & 8 deletions src/multisig/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ pub enum KeyPair {
}

impl KeyPair {
pub fn generate(
/// Create a new `KeyPair` from the provided `SecretRecoveryKey` and `session_nonce` deterministically, for the given `algorithm`.
pub fn new(
secret_recovery_key: &SecretRecoveryKey,
session_nonce: &[u8],
algorithm: Algorithm,
Expand All @@ -35,18 +36,16 @@ impl KeyPair {

pub fn encoded_verifying_key(&self) -> Vec<u8> {
match self {
Self::Ecdsa(key_pair) => key_pair.encoded_verifying_key().to_vec(),
Self::Ed25519(key_pair) => key_pair.encoded_verifying_key().to_vec(),
Self::Ecdsa(key_pair) => key_pair.encoded_verifying_key().into(),
Self::Ed25519(key_pair) => key_pair.encoded_verifying_key().into(),
}
}

pub fn sign(&self, msg_to_sign: &MessageDigest) -> TofndResult<Vec<u8>> {
match self {
Self::Ecdsa(key_pair) => ecdsa::sign(key_pair.signing_key(), msg_to_sign)
.map_err(|_| anyhow!("signing failed")),
Self::Ed25519(key_pair) => {
ed25519::sign(key_pair, msg_to_sign).map_err(|_| anyhow!("signing failed"))
}
Self::Ecdsa(key_pair) => ecdsa::sign(key_pair.signing_key(), msg_to_sign),
Self::Ed25519(key_pair) => ed25519::sign(key_pair, msg_to_sign),
}
.map_err(|_| anyhow!("signing failed"))
}
}
10 changes: 6 additions & 4 deletions src/multisig/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ use crate::proto;

use tracing::{error, info};

/// Gg20Service
/// `MultisigService` is a gRPC service wrapper around tofn's keygen and signing functions
#[derive(Clone)]
pub struct MultisigService {
pub(super) kv_manager: KvManager,
}

/// create a new Multisig gRPC server
pub fn new_service(kv_manager: KvManager) -> impl proto::multisig_server::Multisig {
MultisigService { kv_manager }
/// Create a new Multisig gRPC server
impl MultisigService {
pub fn new(kv_manager: KvManager) -> Self {
Self { kv_manager }
}
}

#[tonic::async_trait]
Expand Down
11 changes: 5 additions & 6 deletions src/multisig/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ use tofn::sdk::api::SecretRecoveryKey;

impl MultisigService {
pub(super) async fn handle_sign(&self, request: &SignRequest) -> TofndResult<Vec<u8>> {
// re-generate secret key from seed, then sign
let algorithm = Algorithm::from_i32(request.algorithm)
.ok_or(anyhow!("Invalid algorithm: {}", request.algorithm))?;
.ok_or_else(|| anyhow!("Invalid algorithm: {}", request.algorithm))?;

// re-generate secret key from seed, then sign
let secret_recovery_key = self
.find_matching_seed(&request.key_uid, &request.pub_key, algorithm)
.await?;

let key_pair =
KeyPair::generate(&secret_recovery_key, request.key_uid.as_bytes(), algorithm)
.map_err(|_| anyhow!("key re-generation failed"))?;
let key_pair = KeyPair::new(&secret_recovery_key, request.key_uid.as_bytes(), algorithm)
.map_err(|_| anyhow!("key re-generation failed"))?;

let signature = key_pair
.sign(&request.msg_to_sign.as_slice().try_into()?)
Expand Down Expand Up @@ -53,7 +52,7 @@ impl MultisigService {
for seed_key in seed_key_iter {
let secret_recovery_key = self.kv_manager.get_seed(&seed_key).await?;

let key_pair = KeyPair::generate(&secret_recovery_key, key_uid.as_bytes(), algorithm)
let key_pair = KeyPair::new(&secret_recovery_key, key_uid.as_bytes(), algorithm)
.map_err(|_| anyhow!("key re-generation failed"))?;

if pub_key == key_pair.encoded_verifying_key() {
Expand Down
Loading

0 comments on commit 6eba099

Please sign in to comment.