Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TSS key generation and signing examples #26

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
16 changes: 16 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ authors = [
"Gary <[email protected]>"
]

edition="2018"

[lib]
crate-type = ["rlib", "dylib"]

Expand All @@ -17,11 +19,25 @@ serde_json = "1.0"
serde_derive = "1.0"
rand = "0.8"
sha2 = "0.9"
zeroize = "1"


[dev-dependencies]
ed25519-dalek = "1.0.1"
rand_xoshiro = "0.6.0"
itertools = "0.10"
rocket = { version = "0.5.0-rc.1", features = ["json"] }
reqwest = { version = "0.9", default-features = false }
aes-gcm = "0.9.4"
uuid = { version = "0.8", features = ["v4"] }
hmac = "0.11"


[features]
default = ["curv/rust-gmp-kzen"]

[[example]]
name = "sm_manager"

[[example]]
name = "sign_client"
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,40 @@ Rust implementation of multiparty Ed25519 signature scheme.

The above protocols are for Schnorr signature system. EdDSA is a variant of Schnorr signature system with (possibly twisted) Edwards curves. We adopt the multi party implementations to follow Ed25519 methods for private key and public key generation according to [RFC8032](https://tools.ietf.org/html/rfc8032#section-5.1)

## Run GG18 Demo

The following steps are for setup, key generation with `n` parties and signing with `t+1` parties.

### Setup

1. We use shared state machine architecture (see [white city](https://github.com/KZen-networks/white-city)). The parameters `parties` and `threshold` can be configured by changing the file: `param.json`. A keygen will run with `parties` parties and signing will run with any subset of `threshold + 1` parties. `param.json` file should be located in the same path of the client software.

2. Install [Rust](https://rustup.rs/). Run `cargo build --release --examples` (it will build into `/target/release/examples/`)

3. Run the shared state machine: `./sm_manager`. By default, it's configured to be in `127.0.0.1:8000`, this can be changed in `Rocket.toml` file. The `Rocket.toml` file should be in the same folder you run `sm_manager` from.

### KeyGen

run `gg18_keygen_client` as follows: `./gg18_keygen_client http://127.0.0.1:8000 keys.store`. Replace IP and port with the ones configured in setup. Once `n` parties join the application will run till finish. At the end each party will get a local keys file `keys.store` (change filename in command line). This contains secret and public data of the party after keygen. The file therefore should remain private.

### Sign

Run `./sign_client`. The application takes three arguments: `IP:port` as in keygen, `filename` and message to be signed: `./sign_client http://127.0.0.1:8001 keys.store "KZen Networks"`. The same message should be used by all signers. Once `t+1` parties join the protocol will run and will output to screen signature (R,s).

The `./sign_client` executable initially tries to unhex its input message (the third parameter). Before running ensure two things:

1. If you want to pass a binary message to be signed - hex it.
2. If you want to pass a textual message in a non-hex form, make sure it can't be unhexed.
Simply put, the safest way to use the signing binary is to just always hex your messages before passing them to the `./sign_client` executable.

#### Example
To sign the message `hello world`, first calculate its hexadecimal representation. This yields the `68656c6c6f20776f726c64`.
Then, run:
```bash
./sign_client http://127.0.0.1:8000 keys.store "68656c6c6f20776f726c64"
```


License
-------
This library is released under the terms of the GPL-3.0 license. See [LICENSE](LICENSE) for more information.
Expand Down
204 changes: 204 additions & 0 deletions examples/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
use std::{env, iter::repeat, thread, time, time::Duration};

use aes_gcm::{Aes256Gcm, Nonce};
use aes_gcm::aead::{NewAead, Aead, Payload};

use reqwest::Client;
use serde::{Deserialize, Serialize};

pub type Key = String;

#[allow(dead_code)]
pub const AES_KEY_BYTES_LEN: usize = 32;

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct AEAD {
pub ciphertext: Vec<u8>,
pub tag: Vec<u8>,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct PartySignup {
pub number: u16,
pub uuid: String,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct Index {
pub key: Key,
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct Entry {
pub key: Key,
pub value: String,
}

#[derive(Serialize, Deserialize)]
pub struct Params {
pub parties: String,
pub threshold: String,
}

#[allow(dead_code)]
pub fn aes_encrypt(key: &[u8], plaintext: &[u8]) -> AEAD {

let aes_key = aes_gcm::Key::from_slice(key);
let cipher = Aes256Gcm::new(aes_key);

let nonce_vector: Vec<u8> = repeat(3).take(12).collect();
let nonce = Nonce::from_slice(nonce_vector.as_slice());

let out_tag: Vec<u8> = repeat(0).take(16).collect();

let text_payload = Payload {
msg: plaintext,
aad: &out_tag.as_slice()
};

let ciphertext = cipher.encrypt(nonce, text_payload)
.expect("encryption failure!");

AEAD {
ciphertext: ciphertext,
tag: out_tag.to_vec(),
}
}

#[allow(dead_code)]
pub fn aes_decrypt(key: &[u8], aead_pack: AEAD) -> Vec<u8> {

let aes_key = aes_gcm::Key::from_slice(key);

let nonce_vector: Vec<u8> = repeat(3).take(12).collect();
let nonce = Nonce::from_slice(nonce_vector.as_slice());

let gcm = Aes256Gcm::new(aes_key);

let text_payload = Payload {
msg: aead_pack.ciphertext.as_slice(),
aad: aead_pack.tag.as_slice()
};

let out = gcm.decrypt(nonce, text_payload);
out.unwrap()
}

pub fn postb<T>(client: &Client, path: &str, body: T) -> Option<String>
where
T: serde::ser::Serialize,
{
let addr = env::args()
.nth(1)
.unwrap_or_else(|| "http://127.0.0.1:8001".to_string());
let retries = 3;
let retry_delay = time::Duration::from_millis(250);
for _i in 1..retries {
let res = client
.post(&format!("{}/{}", addr, path))
.json(&body)
.send();

if let Ok(mut res) = res {
return Some(res.text().unwrap());
}
thread::sleep(retry_delay);
}
None
}

pub fn broadcast(
client: &Client,
party_num: u16,
round: &str,
data: String,
sender_uuid: String,
) -> Result<(), ()> {
let key = format!("{}-{}-{}", party_num, round, sender_uuid);
let entry = Entry {
key: key.clone(),
value: data,
};

let res_body = postb(&client, "set", entry).unwrap();
serde_json::from_str(&res_body).unwrap()
}

pub fn sendp2p(
client: &Client,
party_from: u16,
party_to: u16,
round: &str,
data: String,
sender_uuid: String,
) -> Result<(), ()> {
let key = format!("{}-{}-{}-{}", party_from, party_to, round, sender_uuid);

let entry = Entry {
key: key.clone(),
value: data,
};

let res_body = postb(&client, "set", entry).unwrap();
serde_json::from_str(&res_body).unwrap()
}

pub fn poll_for_broadcasts(
client: &Client,
party_num: u16,
n: u16,
delay: Duration,
round: &str,
sender_uuid: String,
) -> Vec<String> {
let mut ans_vec = Vec::new();
for i in 1..=n {
if i != party_num {
let key = format!("{}-{}-{}", i, round, sender_uuid);
let index = Index { key };
loop {
// add delay to allow the server to process request:
thread::sleep(delay);
let res_body = postb(client, "get", index.clone()).unwrap();
let answer: Result<Entry, ()> = serde_json::from_str(&res_body).unwrap();
if let Ok(answer) = answer {
ans_vec.push(answer.value);
println!("[{:?}] party {:?} => party {:?}", round, i, party_num);
break;
}
}
}
}
ans_vec
}

pub fn poll_for_p2p(
client: &Client,
party_num: u16,
n: u16,
delay: Duration,
round: &str,
sender_uuid: String,
) -> Vec<String> {
let mut ans_vec = Vec::new();
for i in 1..=n {
if i != party_num {
let key = format!("{}-{}-{}-{}", i, party_num, round, sender_uuid);
let index = Index { key };
loop {
// add delay to allow the server to process request:
thread::sleep(delay);
let res_body = postb(client, "get", index.clone()).unwrap();
let answer: Result<Entry, ()> = serde_json::from_str(&res_body).unwrap();
if let Ok(answer) = answer {
ans_vec.push(answer.value);
println!("[{:?}] party {:?} => party {:?}", round, i, party_num);
break;
}
}
}
}
ans_vec
}

fn main(){}
Loading