Skip to content
Open
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 <gary@kzencorp.com>"
]

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