Skip to content

Commit

Permalink
feat(grpc) add simple multisig (#211)
Browse files Browse the repository at this point in the history
* Update to latest tofn

* rename ServiceKv->KvManager

* Init kv manager out of gg20 service

* Rename gg20.kv->gg20.kv_manager

* Handle mnemonic from kv manager

* Move mnemonic mod at the root level

* implement multisig service

* small changes in handle keygen

* Add basic multisig keygen test

* rm wild import

* Add basic multisig sign test

* Use local tofn

* Use updated `encoded_verifying_key()`

* update protobuf

* Add some todos

* point to remote tofn

* throw error if internal handlers don't succeed

* Use single-char shorts

https://docs.rs/clap/2.33.0/clap/struct.Arg.html#method.short

Sadly "m" is reserved by mnemonic

* Add TODO for better service shutdown

* use default port values for default config

* remove comment

* move type declaration after use *

* better comment in main.rs

Co-authored-by: Gus Gutoski <[email protected]>

* point to tofn main

* Revert "better comment in main.rs"

This reverts commit a58cf04.

* add party-uid in sign

* spin up gg20 and multisig services under the same grpc server

* use protobuf main

* Add comments in keygen and sign

* log keygen and sign result

* fix: query key_uid instead or party_uid

duh

* update tofn main

* add tests for multisig

* Fix message digest size in tests

* Update src/main.rs

Co-authored-by: Gus Gutoski <[email protected]>

* [no ci] add some comments in tests

* [no ci] Update src/multisig/service.rs

Co-authored-by: Milap Sheth <[email protected]>

* [no ci] Update src/multisig/service.rs

Co-authored-by: Milap Sheth <[email protected]>

Co-authored-by: Gus Gutoski <[email protected]>
Co-authored-by: Milap Sheth <[email protected]>
  • Loading branch information
3 people authored Oct 21, 2021
1 parent 117a35b commit 0a70c4b
Show file tree
Hide file tree
Showing 31 changed files with 612 additions and 176 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::configure()
// .build_client(false)
// .out_dir(".") // if you want to peek at the generated code
.compile(&["proto/grpc.proto"], &["proto"])?;
.compile(&["proto/grpc.proto", "proto/multisig.proto"], &["proto"])?;
Ok(())
}
2 changes: 1 addition & 1 deletion proto
Submodule proto updated 1 files
+35 −0 multisig.proto
14 changes: 8 additions & 6 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use clap::{App, Arg};

// error handling
use crate::{encrypted_sled::PasswordMethod, gg20::mnemonic::Cmd, TofndResult};
use crate::{encrypted_sled::PasswordMethod, mnemonic::Cmd, TofndResult};
use anyhow::anyhow;

// TODO: move these into constants.rs
const DEFAULT_PATH_ROOT: &str = ".tofnd";
const TOFND_HOME_ENV_VAR: &str = "TOFND_HOME";
const DEFAULT_MNEMONIC_CMD: &str = "existing";
const DEFAULT_PORT: &str = "50051";
const DEFAULT_PORT: u16 = 50051;
const AVAILABLE_MNEMONIC_CMDS: [&str; 4] = ["existing", "create", "import", "export"];

#[cfg(feature = "malicious")]
Expand All @@ -30,7 +30,7 @@ pub struct Config {
impl Default for Config {
fn default() -> Self {
Config {
port: 50051,
port: DEFAULT_PORT,
safe_keygen: true,
mnemonic_cmd: Cmd::Existing,
tofnd_path: DEFAULT_PATH_ROOT.to_string(),
Expand All @@ -42,14 +42,17 @@ impl Default for Config {
}

pub fn parse_args() -> TofndResult<Config> {
// need to use let to avoid dropping temporary value
let port = &DEFAULT_PORT.to_string();

let app = App::new("tofnd")
.about("A threshold signature scheme daemon")
.arg(
Arg::with_name("port")
.long("port")
.short("p")
.required(false)
.default_value(DEFAULT_PORT),
.default_value(port),
)
.arg(
// TODO: change to something like `--unsafe-primes`
Expand Down Expand Up @@ -105,6 +108,7 @@ pub fn parse_args() -> TofndResult<Config> {
let behaviours = get_behaviour_matches(app.clone())?;

let matches = app.get_matches();

let port = matches
.value_of("port")
.ok_or_else(|| anyhow!("port value"))?
Expand All @@ -115,12 +119,10 @@ pub fn parse_args() -> TofndResult<Config> {
.ok_or_else(|| anyhow!("cmd value"))?
.to_string();
let mnemonic_cmd = Cmd::from_string(&mnemonic_cmd)?;

let tofnd_path = matches
.value_of("directory")
.ok_or_else(|| anyhow!("directory value"))?
.to_string();

let password_method = match matches.is_present("no-password") {
true => PasswordMethod::NoPassword,
false => PasswordMethod::Prompt,
Expand Down
4 changes: 2 additions & 2 deletions src/gg20/key_presence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ impl Gg20Service {
request: proto::KeyPresenceRequest,
) -> TofndResult<proto::key_presence_response::Response> {
// check if mnemonic is available
let _ = self.seed().await?;
let _ = self.kv_manager.seed().await?;

// check if requested key exists
if self.kv.exists(&request.key_uid).await? {
if self.kv_manager.kv().exists(&request.key_uid).await? {
info!(
"Found session-id {} in kv store during key presence check",
request.key_uid
Expand Down
5 changes: 3 additions & 2 deletions src/gg20/keygen/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use super::{
types::{KeygenInitSanitized, MAX_PARTY_SHARE_COUNT, MAX_TOTAL_SHARE_COUNT},
Gg20Service,
};
use crate::kv_manager::types::KeyReservation;
use crate::kv_manager::KeyReservation;

impl Gg20Service {
/// Receives a message from the stream and tries to handle keygen init operations.
Expand Down Expand Up @@ -73,7 +73,8 @@ impl Gg20Service {

// reserve key
let key_uid_reservation = self
.kv
.kv_manager
.kv()
.reserve_key(keygen_init.new_key_uid.clone())
.await
.map_err(|err| anyhow!("failed to reseve key: {}", err))?;
Expand Down
2 changes: 1 addition & 1 deletion src/gg20/keygen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl Gg20Service {
let mut aggregator_receivers = Vec::with_capacity(my_share_count);

// computation of (party_keypair, party_zksetup) is intensive so we compute them here once
let secret_recovery_key = self.seed().await?;
let secret_recovery_key = self.kv_manager.seed().await?;
let session_nonce = keygen_init.new_key_uid.as_bytes();

info!("Generating keypair for party {} ...", keygen_init.my_index);
Expand Down
14 changes: 9 additions & 5 deletions src/gg20/keygen/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use super::{
types::{BytesVec, KeygenInitSanitized, TofnKeygenOutput, TofndKeygenOutput},
Gg20Service,
};
use crate::{gg20::types::PartyInfo, kv_manager::types::KeyReservation};
use crate::{gg20::types::PartyInfo, kv_manager::KeyReservation};

// tonic cruft
use tokio::sync::{
Expand All @@ -38,7 +38,10 @@ impl Gg20Service {
let keygen_outputs = match Self::aggregate_keygen_outputs(aggregator_receivers).await {
Ok(keygen_outputs) => keygen_outputs,
Err(err) => {
self.kv.unreserve_key(key_uid_reservation).await;
self.kv_manager
.kv()
.unreserve_key(key_uid_reservation)
.await;
return Err(anyhow!(
"Error at Keygen output aggregation. Unreserving key {}",
err
Expand All @@ -63,7 +66,8 @@ impl Gg20Service {
);

// try to put data inside kv store
self.kv
self.kv_manager
.kv()
.put(key_uid_reservation, kv_data.into())
.await
.map_err(|err| anyhow!(err))?;
Expand Down Expand Up @@ -106,7 +110,7 @@ impl Gg20Service {

// check that all shares returned the same public key and group recover info
let share_id = secret_key_shares[0].share().index();
let pub_key = secret_key_shares[0].group().pubkey_bytes();
let pub_key = secret_key_shares[0].group().encoded_pubkey();
let group_info = secret_key_shares[0]
.group()
.all_shares_bytes()
Expand All @@ -116,7 +120,7 @@ impl Gg20Service {
// Here we check that the first share produced the same info as the i-th.
for secret_key_share in &secret_key_shares[1..] {
// try to get pubkey of i-th share. Each share should produce the same pubkey
if pub_key != secret_key_share.group().pubkey_bytes() {
if pub_key != secret_key_share.group().encoded_pubkey() {
return Err(anyhow!(
"Party {}'s share {} and {} returned different public key",
keygen_init.my_index,
Expand Down
1 change: 0 additions & 1 deletion src/gg20/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use tracing::{error, info, span, Level};
mod broadcast;
mod key_presence;
mod keygen;
pub mod mnemonic;
mod protocol;
mod recover;
pub mod service;
Expand Down
11 changes: 7 additions & 4 deletions src/gg20/recover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ impl Gg20Service {

// check if key-uid already exists in kv-store. If yes, return success and don't update the kv-store
if self
.kv
.kv_manager
.kv()
.exists(&keygen_init.new_key_uid)
.await
.map_err(|err| anyhow!(err))?
Expand All @@ -49,7 +50,7 @@ impl Gg20Service {

// recover secret key shares from request
// get mnemonic seed
let secret_recovery_key = self.seed().await?;
let secret_recovery_key = self.kv_manager.seed().await?;
let secret_key_shares = self
.recover_secret_key_shares(&secret_recovery_key, &keygen_init, &keygen_output)
.map_err(|err| anyhow!("Failed to acquire secret key share {}", err))?;
Expand Down Expand Up @@ -146,7 +147,8 @@ impl Gg20Service {
) -> TofndResult<()> {
// try to make a reservation
let reservation = self
.kv
.kv_manager
.kv()
.reserve_key(keygen_init_sanitized.new_key_uid)
.await
.map_err(|err| anyhow!("failed to complete reservation: {}", err))?;
Expand All @@ -159,7 +161,8 @@ impl Gg20Service {
);
// try writing the data to the kv-store
Ok(self
.kv
.kv_manager
.kv()
.put(reservation, kv_data.into())
.await
.map_err(|err| anyhow!("failed to update kv store: {}", err))?)
Expand Down
27 changes: 4 additions & 23 deletions src/gg20/service/mod.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,20 @@
//! This mod includes the service implementation derived from
use super::mnemonic::FileIo;
use super::proto;
use super::types::{ServiceKv, DEFAULT_KV_NAME};
use crate::config::Config;
use crate::encrypted_sled::Password;
use std::path::PathBuf;

// error handling
use crate::TofndResult;
use anyhow::anyhow;
use crate::kv_manager::KvManager;

#[cfg(feature = "malicious")]
pub mod malicious;

/// Gg20Service
#[derive(Clone)]
pub struct Gg20Service {
pub(super) kv: ServiceKv,
pub(super) io: FileIo,
pub(super) kv_manager: KvManager,
pub(super) cfg: Config,
}

/// create a new Gg20 gRPC server
pub async fn new_service(
cfg: Config,
password: Password,
) -> TofndResult<impl proto::gg20_server::Gg20> {
let kv = ServiceKv::new(&cfg.tofnd_path, DEFAULT_KV_NAME, password)
.map_err(|err| anyhow!("Shares KV store error: {}", err))?;

let io = FileIo::new(PathBuf::from(&cfg.tofnd_path));

let gg20 = Gg20Service { kv, io, cfg };

gg20.handle_mnemonic().await?;
Ok(gg20)
pub fn new_service(cfg: Config, kv_manager: KvManager) -> impl proto::gg20_server::Gg20 {
Gg20Service { kv_manager, cfg }
}
2 changes: 1 addition & 1 deletion src/gg20/sign/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl Gg20Service {
};

// try to get party info related to session id
let party_info: PartyInfo = match self.kv.get(&sign_init.key_uid).await {
let party_info: PartyInfo = match self.kv_manager.kv().get(&sign_init.key_uid).await {
Ok(value) => value.try_into()?,
Err(err) => {
// if no such session id exists, send a message to client that indicates that recovery is needed and stop sign
Expand Down
58 changes: 1 addition & 57 deletions src/gg20/types.rs
Original file line number Diff line number Diff line change
@@ -1,68 +1,12 @@
//! Helper structs and implementations for [crate::gg20].
use std::convert::TryFrom;

// zeroize Entropy and Password
use zeroize::Zeroize;

use tracing::{info, span, Level, Span};

use crate::kv_manager::{error::KvError, kv::Kv};

pub(super) type MessageDigest = tofn::gg20::sign::MessageDigest;

// default KV store names
pub(super) const DEFAULT_KV_NAME: &str = "kv";

#[derive(Serialize, Deserialize, Debug, Clone)]
pub(super) enum KvValue {
PartyInfo(PartyInfo),
Entropy(Entropy),
}

/// Create KvValue from PartyInfo
impl From<PartyInfo> for KvValue {
fn from(v: PartyInfo) -> KvValue {
KvValue::PartyInfo(v)
}
}

/// Create KvValue from Entropy
impl From<Entropy> for KvValue {
fn from(v: Entropy) -> KvValue {
KvValue::Entropy(v)
}
}

/// Create PartyInfo from KvValue
impl TryFrom<KvValue> for PartyInfo {
type Error = KvError;
fn try_from(v: KvValue) -> Result<Self, Self::Error> {
match v {
KvValue::PartyInfo(party_info) => Ok(party_info),
KvValue::Entropy(_) => Err(Self::Error::ValueTypeErr(
"Expecting PartyInfo, got Entropy".to_string(),
)),
}
}
}

/// Create Entropy from KvValue
impl TryFrom<KvValue> for Entropy {
type Error = KvError;
fn try_from(v: KvValue) -> Result<Self, Self::Error> {
match v {
KvValue::PartyInfo(_) => Err(Self::Error::ValueTypeErr(
"Expecting Entropy, got PartyInfo".to_string(),
)),
KvValue::Entropy(entropy) => Ok(entropy),
}
}
}

/// Kv store for gg20 service
pub(super) type ServiceKv = Kv<KvValue>;

/// Mnemonic type needs to be known globaly to create/access the mnemonic kv store
#[derive(Zeroize, Debug, Clone, Serialize, Deserialize)]
#[zeroize(drop)]
Expand Down Expand Up @@ -101,7 +45,7 @@ pub(super) struct TofndInfo {

/// `KeyShareKv` record
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct PartyInfo {
pub struct PartyInfo {
pub(super) common: GroupPublicInfo,
pub(super) shares: Vec<ShareSecretInfo>,
pub(super) tofnd: TofndInfo,
Expand Down
8 changes: 5 additions & 3 deletions src/kv_manager/kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use super::{
sled_bindings::{handle_exists, handle_get, handle_put, handle_reserve},
types::{
Command::{self, *},
KeyReservation, DEFAULT_KV_PATH,
KeyReservation, DEFAULT_KV_NAME, DEFAULT_KV_PATH,
},
};
use serde::{de::DeserializeOwned, Serialize};
Expand All @@ -31,8 +31,10 @@ where
{
/// Creates a new kv service. Returns [InitErr] on failure.
/// the path of the kvstore is `root_path` + "/kvstore/" + `kv_name`
pub fn new(root_path: &str, kv_name: &str, password: Password) -> KvResult<Self> {
let kv_path = PathBuf::from(root_path).join(DEFAULT_KV_PATH).join(kv_name);
pub fn new(root_path: &str, password: Password) -> KvResult<Self> {
let kv_path = PathBuf::from(root_path)
.join(DEFAULT_KV_PATH)
.join(DEFAULT_KV_NAME);
// use to_string_lossy() instead of to_str() to avoid handling Option<&str>
let kv_path = kv_path.to_string_lossy().to_string();
Self::with_db_name(kv_path, password)
Expand Down
9 changes: 7 additions & 2 deletions src/kv_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
/// Custom error types for [kv] and [sled_bindings]
pub mod error;
/// public API of kv manager
pub mod kv;
mod kv;
/// sled bindings for basic kv operations
mod sled_bindings;
/// definition of kv_manager types and default paths
pub mod types;
mod types;
/// wrapers for values stored by tofnd services
mod value;

pub use types::KeyReservation;
pub use value::KvManager;

// tests for low-level operations
#[cfg(test)]
Expand Down
Loading

0 comments on commit 0a70c4b

Please sign in to comment.