From cf23cfd6e280dc804c0aec4587ed4c7e57af1837 Mon Sep 17 00:00:00 2001 From: maurges Date: Tue, 17 Mar 2026 17:37:44 +0100 Subject: [PATCH 1/5] Add readme. Add reexports for better examples --- README.md | 99 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 19 ++++++---- src/lowlevel.rs | 4 +- 3 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..3fa1af9 --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ +# tECDH - threshold elliptic-curve Diffie-Hellman key exchange + +This crate implements tECDH - when one of the Diffie-Hellman keys is kept as +key shares. Multiple parties, when activated with a common +`counterparty_public_key`, will execute the protocol to obtain a DH session +key. + +The procedure for running this protocol resembles tBLS signatures, and in +fact was directly adpated from . A big +difference from BLS is that since we don't need signature verification, we +don't need efficient pairings and can use any curve we want. + +## How to use the library + +In short, you will need to load the key shares, establish network connection +between parties, obtain a shared execution id, match the party indicies, and +then commence the protocol. + +```rust +// Curve of your choice +use E = tecdh::generic_ec::curves::Ed25519; +// The unique execution ID which the parties agree to before starting +let execution_id: Vec = todo!(); +// The key of the counterparty you're performing the ECDH handshake with +let counterparty: tecdh::generic_ec::NonZero> = todo!(); +// The key share that was generated in advance and is now loaded from +// persistent storage +let key_share: tecdh::key_share::CoreKeyShare = todo!(); +// The network setup using `round-based` +let incoming: tecdh::round_based::Stream> = todo!(); +let outgoing: tecdh::round_based::Sink> = todo!(); +let party = tecdh::round_based::MpcParty::connected((incoming, outgoing)) = todo!(); +// Match party indicies from keygen to the current participants (not used for +additive key shares) +let participant_indicies: &[u16] = todo!(); +let this_party_index: u16 = todo!(); + +// Run the protocol +let session_key = tecdh::start::( + execution_id, + counterparty, + this_party_index, + key_share, + participant_indicies, + party, + &mut rand::rngs::OsRng, +).await.unwrap(); +``` + +### Distributed key generation + +First of all, you will need to generate a key that the distributed parties will +use. For that purpose, you can use any secure DKG protocol. We recommend using +[cggmp21-keygen](https://docs.rs/cggmp21-keygen/0.5.0/cggmp21_keygen/) --- it's +an interactive protocol built on the same foundations of `round-based` and +`generic-ec`. + +### Networking + +This library relies on [`round-based`](https://crates.io/crates/round-based) +for the MPC backend, which means the networking interface should be constructed +using its primitives, which are in practice the `Sink` and `Stream` types from +`futures_core`. + +The exact underlying mechanism behind the sink and stream depend on the +application and are not provided here. Some application will use libp2p, others +may want to use a message broker like kafka or postgres. +No matter the implementation, all messages need to be authenticated --- when +one party receives a message from another, it needs to be able to verify that +the message comes from the claimed sender. + +### Signer indicies + +We use indices to uniquely refer to particular signers sharing a key. Each +index `i` is an unsigned integer `u16` with `0 ≤ i < n` where `n` is the total +number of participants in the protocol. + +All signers should have the same view about each others’ indices. For instance, +if Signer A holds index 2, then all other signers must agree that `i=2` +corresponds to Signer A. These indicies must match between the +keygen an the protocol execution. + +Assuming some sort of PKI (which would anyway likely be used to ensure secure +communication, as described above), each signer has a public key that uniquely +identifies that signer. It is then possible to assign unique indices to the +signers by lexicographically sorting the signers’ public keys, and letting the +index of a signer be the position of that signer’s public key in the sorted +list. + +### Execution ID + +Execution of our protocols requires all participants to agree on unique +execution ID (aka session identifier) that is assumed never to repeat. This +string provides context separation between different executions of the protocol +to ensure that an adversary attack the protocol by replaying messages from one +execution to another. + +## Join us in Discord! +Feel free to reach out to us [in Discord](https://discordapp.com/channels/905194001349627914/1285268686147424388)! diff --git a/src/lib.rs b/src/lib.rs index e20df1a..bca864e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,9 @@ //! This crate implements Threshold Elliptic Curve Diffie-Hellman key //! exchange. //! -//! Threshold means that the private key is shared between multiple parties. -//! Only when a threshold amount of parties run the protocol together, can -//! they form the diffie-hellman session key -//! -//! The procedure for running this protocol resembles tBLS signatures, and in -//! fact was directly adpated from . A big -//! difference from BLS is that since we don't need signature verification, we -//! don't need efficient pairings and can use any curve we want. +//! To run the protocol, you need to call the [`start`] function. See the +//! repository [readme](https://github.com/dfns/LFDT-Lockness/blob/m/README.md) +//! for more information on how to set up the parameters #![warn(missing_docs, unsafe_code, unused_crate_dependencies)] #![cfg_attr( @@ -22,6 +17,14 @@ pub mod lowlevel; /// Helper types for the MPC execution pub mod mpc; +/// Reexport for convenience +pub use generic_ec; +/// Reexport for convenience +pub use key_share; +/// Reexport for convenience +pub use round_based; + + /// Start an MPC protocol that performs threshold ECDH with shared private key. /// Returns the session key /// diff --git a/src/lowlevel.rs b/src/lowlevel.rs index 44914f2..3dce119 100644 --- a/src/lowlevel.rs +++ b/src/lowlevel.rs @@ -79,8 +79,8 @@ pub fn partial_ecdh( /// the same order by participant as `partials` /// /// Partials, public shares and share preimages each should correspond to the -/// same party, that is all be ordered in the same way by the index of the party -/// they come from. +/// same party, that is, they should all be ordered in the same way by the index +/// of the party they come from. /// /// In paper this function is called `Combine(pk, VK, x, E)`, section IV.A pub fn aggregate( From f29eb71f0a07be5b5dadbb5a124e5f4261c0efcb Mon Sep 17 00:00:00 2001 From: maurges Date: Tue, 17 Mar 2026 17:45:53 +0100 Subject: [PATCH 2/5] Make serde optional --- Cargo.lock | 81 ++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 12 +++++++- src/lib.rs | 1 - src/lowlevel.rs | 8 +++-- src/mpc.rs | 16 +++++++--- 5 files changed, 109 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aabd145..f57e817 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addchain" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e33f6a175ec6a9e0aca777567f9ff7c3deefc255660df887e7fa3585e9801d8" +dependencies = [ + "num-bigint 0.3.3", + "num-integer", + "num-traits", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -202,7 +213,7 @@ version = "3.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "proc-macro2", "quote", @@ -215,10 +226,27 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ + "byteorder", + "ff_derive", "rand_core", "subtle", ] +[[package]] +name = "ff_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f10d12652036b0e99197587c6ba87a8fc3031986499973c030d8b44fcc151b60" +dependencies = [ + "addchain", + "num-bigint 0.3.3", + "num-integer", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "fiat-crypto" version = "0.2.9" @@ -319,8 +347,10 @@ dependencies = [ "generic-ec-core", "group", "k256", + "p256", "rand_core", "sha2", + "stark-curve", "subtle", "zeroize", ] @@ -368,6 +398,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + [[package]] name = "ident_case" version = "1.0.1" @@ -403,6 +439,17 @@ version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -431,6 +478,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "elliptic-curve", + "primeorder", +] + [[package]] name = "phantom-type" version = "0.3.1" @@ -470,6 +527,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.91" @@ -645,6 +711,19 @@ dependencies = [ "digest", ] +[[package]] +name = "stark-curve" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad5e4452423e6bceebf2ecb2c4680f7159a62196e3e6b0ffb824b8d59c3fc90" +dependencies = [ + "ff", + "hex-literal", + "primeorder", + "subtle", + "zeroize", +] + [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index fd22f9c..033968c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,11 @@ generic-ec-zkp = { version = "0.4.4", features = ["serde", "udigest"] } key-share = "0.6" rand_core = { version = "0.6", default-features = false } round-based = { version = "0.4", features = ["derive"] } -serde = "1" thiserror = { version = "2" } udigest = { version = "0.2", default-features = false, features = ["inline-struct", "derive"] } +serde = { version = "1", optional = true } + [dev-dependencies] generic-ec = { version = "0.4.5", features = ["curve-ed25519"] } key-share = { version = "0.6", features = ["spof"] } @@ -22,3 +23,12 @@ rand_dev = "0.1" round-based = { version = "0.4", features = ["sim"] } sha2 = "0.10" test-case = "3" + +[features] +default = ["serde"] + +serde = ["dep:serde", "generic-ec/serde", "generic-ec-zkp/serde"] + +curve-secp256k1 = ["generic-ec/curve-secp256k1"] +curve-secp256r1 = ["generic-ec/curve-secp256r1"] +curve-stark = ["generic-ec/curve-stark"] diff --git a/src/lib.rs b/src/lib.rs index bca864e..0a9f5ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,6 @@ pub use key_share; /// Reexport for convenience pub use round_based; - /// Start an MPC protocol that performs threshold ECDH with shared private key. /// Returns the session key /// diff --git a/src/lowlevel.rs b/src/lowlevel.rs index 3dce119..98797e3 100644 --- a/src/lowlevel.rs +++ b/src/lowlevel.rs @@ -11,8 +11,12 @@ pub fn ecdh( /// Evaluation of partial ECDH as outputted by parties, computed by /// [`partial_ecdh`]. `t` partials can be aggregated with [`aggregate`] -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -#[serde(bound = "")] +#[derive(Clone, Debug)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(bound = "") +)] pub struct PartialEvaluation { /// Index of evaluating party pub i: u16, diff --git a/src/mpc.rs b/src/mpc.rs index f70c01a..5d2de1a 100644 --- a/src/mpc.rs +++ b/src/mpc.rs @@ -1,16 +1,24 @@ use crate::lowlevel; /// Protocol message -#[derive(round_based::ProtocolMessage, Clone, serde::Serialize, serde::Deserialize)] -#[serde(bound = "")] +#[derive(round_based::ProtocolMessage, Clone)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(bound = "") +)] pub enum Msg { /// The only round Partial(MsgPartial), } /// Protocol message -#[derive(Clone, serde::Serialize, serde::Deserialize)] -#[serde(bound = "")] +#[derive(Clone)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(bound = "") +)] pub struct MsgPartial { /// Partial evaluation of party pub evaluation: lowlevel::PartialEvaluation, From e7a9edc81df3bb790ff8ad48bd9a0719ef614846 Mon Sep 17 00:00:00 2001 From: maurges Date: Wed, 18 Mar 2026 11:06:42 +0100 Subject: [PATCH 3/5] Bump version. Clarify one doc --- .github/changelog.sh | 2 +- Cargo.lock | 2 +- Cargo.toml | 2 +- changelog.md | 4 ++++ src/lib.rs | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/changelog.sh b/.github/changelog.sh index fa3c35a..f89ec47 100755 --- a/.github/changelog.sh +++ b/.github/changelog.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash m_branch=m; -changelog_file=CHANGELOG.md; +changelog_file=changelog.md; # fetch master since we might be in a shallow clone git fetch origin "$m_branch:$m_branch" --depth=1 diff --git a/Cargo.lock b/Cargo.lock index f57e817..76d7fd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -760,7 +760,7 @@ dependencies = [ [[package]] name = "tecdh" -version = "0.1.0" +version = "0.2.0" dependencies = [ "digest", "generic-ec", diff --git a/Cargo.toml b/Cargo.toml index 033968c..104527b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tecdh" -version = "0.1.0" +version = "0.2.0" edition = "2021" [dependencies] diff --git a/changelog.md b/changelog.md index e1ce5d4..e6bf931 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ +# v0.2.0 + +Serde support is now behind a feature flag (that is enabled by default) + # v0.1.0 Initial implementation diff --git a/src/lib.rs b/src/lib.rs index 0a9f5ef..ef2175f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ )] /// Functions to perform low-level operations. This can be misused, so they are -/// not recommended unless you know how tECDH works +/// ⚠️not recommended⚠️ unless you know how tECDH works pub mod lowlevel; /// Helper types for the MPC execution pub mod mpc; From f9cf8b03428e4ac63ba8451189569f09ca948b13 Mon Sep 17 00:00:00 2001 From: maurges Date: Fri, 20 Mar 2026 10:13:44 +0100 Subject: [PATCH 4/5] lib.rs uses project readme. The example in readme now compiles --- README.md | 36 +++++++++++++++++++++++++++--------- src/lib.rs | 8 +------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3fa1af9..6a7ffa2 100644 --- a/README.md +++ b/README.md @@ -16,35 +16,53 @@ In short, you will need to load the key shares, establish network connection between parties, obtain a shared execution id, match the party indicies, and then commence the protocol. -```rust +```rust,no_run +# async fn _example() { +use tecdh::{round_based, generic_ec}; +# use std::{pin::Pin, task::{Context, Poll}}; +# struct Example(std::marker::PhantomData); +# impl round_based::Stream for Example { +# type Item = T; +# fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { todo!() } +# } +# impl round_based::Sink for Example { +# type Error = std::io::Error; +# fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { todo!() } +# fn start_send(self: Pin<&mut Self>, _: T) -> Result<(), Self::Error> { todo!() } +# fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { todo!() } +# fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { todo!() } +# } // Curve of your choice -use E = tecdh::generic_ec::curves::Ed25519; +type E = generic_ec::curves::Ed25519; // The unique execution ID which the parties agree to before starting let execution_id: Vec = todo!(); // The key of the counterparty you're performing the ECDH handshake with -let counterparty: tecdh::generic_ec::NonZero> = todo!(); +let counterparty: generic_ec::NonZero> = todo!(); // The key share that was generated in advance and is now loaded from // persistent storage let key_share: tecdh::key_share::CoreKeyShare = todo!(); // The network setup using `round-based` -let incoming: tecdh::round_based::Stream> = todo!(); -let outgoing: tecdh::round_based::Sink> = todo!(); -let party = tecdh::round_based::MpcParty::connected((incoming, outgoing)) = todo!(); +fn make_incoming() -> impl round_based::Stream>, std::io::Error>> +# { Example(std::marker::PhantomData) } +fn make_outgoing() -> impl round_based::Sink>, Error = std::io::Error> +# { Example(std::marker::PhantomData) } +let party = round_based::MpcParty::connected((make_incoming(), make_outgoing())); // Match party indicies from keygen to the current participants (not used for -additive key shares) +// additive key shares) let participant_indicies: &[u16] = todo!(); let this_party_index: u16 = todo!(); // Run the protocol let session_key = tecdh::start::( - execution_id, + &execution_id, counterparty, this_party_index, - key_share, + &key_share, participant_indicies, party, &mut rand::rngs::OsRng, ).await.unwrap(); +# } ``` ### Distributed key generation diff --git a/src/lib.rs b/src/lib.rs index ef2175f..5760641 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,4 @@ -//! This crate implements Threshold Elliptic Curve Diffie-Hellman key -//! exchange. -//! -//! To run the protocol, you need to call the [`start`] function. See the -//! repository [readme](https://github.com/dfns/LFDT-Lockness/blob/m/README.md) -//! for more information on how to set up the parameters - +#![doc = include_str!("../README.md")] #![warn(missing_docs, unsafe_code, unused_crate_dependencies)] #![cfg_attr( not(test), From 38184eba051eba237202c9e2d4a9ca335196b7f5 Mon Sep 17 00:00:00 2001 From: maurges Date: Fri, 20 Mar 2026 17:32:57 +0100 Subject: [PATCH 5/5] Fix generic-ec features. Improve readme --- Cargo.lock | 71 +++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 5 ++- README.md | 93 +++++++++++++++++++++++++++--------------------------- src/lib.rs | 3 ++ 4 files changed, 125 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76d7fd8..2d9cb8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -259,12 +259,65 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -283,11 +336,16 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -439,6 +497,12 @@ version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + [[package]] name = "num-bigint" version = "0.3.3" @@ -711,6 +775,12 @@ dependencies = [ "digest", ] +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "stark-curve" version = "0.1.3" @@ -763,6 +833,7 @@ name = "tecdh" version = "0.2.0" dependencies = [ "digest", + "futures", "generic-ec", "generic-ec-zkp", "key-share", diff --git a/Cargo.toml b/Cargo.toml index 104527b..9a806d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] digest = "0.10" -generic-ec = { version = "0.4.5", features = ["serde", "curve-secp256k1", "hash-to-scalar"] } +generic-ec = { version = "0.4.5", features = ["serde", "hash-to-scalar"] } generic-ec-zkp = { version = "0.4.4", features = ["serde", "udigest"] } key-share = "0.6" rand_core = { version = "0.6", default-features = false } @@ -23,6 +23,8 @@ rand_dev = "0.1" round-based = { version = "0.4", features = ["sim"] } sha2 = "0.10" test-case = "3" +# doctests only +futures = "0.3" [features] default = ["serde"] @@ -31,4 +33,5 @@ serde = ["dep:serde", "generic-ec/serde", "generic-ec-zkp/serde"] curve-secp256k1 = ["generic-ec/curve-secp256k1"] curve-secp256r1 = ["generic-ec/curve-secp256r1"] +curve-ed25519 = ["generic-ec/curve-ed25519"] curve-stark = ["generic-ec/curve-stark"] diff --git a/README.md b/README.md index 6a7ffa2..15f7318 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,17 @@ fact was directly adpated from . A big difference from BLS is that since we don't need signature verification, we don't need efficient pairings and can use any curve we want. +## Cargo features + +This crate has the following feature flags: + +- `serde` - enable serde support for the data types exported by this crate. + **Enabled** by default +- `curve-secp256k1` - enable Secp256k1 curve in `generic_ec::curves` +- `curve-secp256r1` - enable Secp256r1 curve in `generic_ec::curves` +- `curve-ed25519` - enable Ed25519 curve in `generic_ec::curves` +- `curve-stark` - enable Stark curve in `generic_ec::curves` + ## How to use the library In short, you will need to load the key shares, establish network connection @@ -17,61 +28,51 @@ between parties, obtain a shared execution id, match the party indicies, and then commence the protocol. ```rust,no_run -# async fn _example() { use tecdh::{round_based, generic_ec}; -# use std::{pin::Pin, task::{Context, Poll}}; -# struct Example(std::marker::PhantomData); -# impl round_based::Stream for Example { -# type Item = T; -# fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { todo!() } -# } -# impl round_based::Sink for Example { -# type Error = std::io::Error; -# fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { todo!() } -# fn start_send(self: Pin<&mut Self>, _: T) -> Result<(), Self::Error> { todo!() } -# fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { todo!() } -# fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { todo!() } -# } // Curve of your choice type E = generic_ec::curves::Ed25519; -// The unique execution ID which the parties agree to before starting -let execution_id: Vec = todo!(); -// The key of the counterparty you're performing the ECDH handshake with -let counterparty: generic_ec::NonZero> = todo!(); -// The key share that was generated in advance and is now loaded from -// persistent storage -let key_share: tecdh::key_share::CoreKeyShare = todo!(); -// The network setup using `round-based` -fn make_incoming() -> impl round_based::Stream>, std::io::Error>> -# { Example(std::marker::PhantomData) } -fn make_outgoing() -> impl round_based::Sink>, Error = std::io::Error> -# { Example(std::marker::PhantomData) } -let party = round_based::MpcParty::connected((make_incoming(), make_outgoing())); -// Match party indicies from keygen to the current participants (not used for -// additive key shares) -let participant_indicies: &[u16] = todo!(); -let this_party_index: u16 = todo!(); - -// Run the protocol -let session_key = tecdh::start::( - &execution_id, - counterparty, - this_party_index, - &key_share, - participant_indicies, - party, - &mut rand::rngs::OsRng, -).await.unwrap(); -# } + +async fn example() -> Result, tecdh::mpc::Error> { + // The unique execution ID which the parties agree to before starting + let execution_id: Vec = todo!(); + // The key of the counterparty you're performing the ECDH handshake with + let counterparty: generic_ec::NonZero> = todo!(); + // The key share that was generated in advance and is now loaded from + // persistent storage + let key_share: tecdh::key_share::CoreKeyShare = todo!(); + // The network setup using `round-based` and `futures` + let incoming = futures::stream::pending::>, std::io::Error>>(); + let outgoing = futures::sink::drain::>>(); + let party = round_based::MpcParty::connected((incoming, outgoing)); + // Match party indicies from keygen to the current participants (not used for + // additive key shares) + let participant_indicies: &[u16] = todo!(); + let this_party_index: u16 = todo!(); + + // Run the protocol + let session_key = tecdh::start::( + &execution_id, + counterparty, + this_party_index, + &key_share, + participant_indicies, + party, + &mut rand::rngs::OsRng, + ).await?; + + // Use the generated session key + Ok(session_key) +} ``` ### Distributed key generation First of all, you will need to generate a key that the distributed parties will use. For that purpose, you can use any secure DKG protocol. We recommend using -[cggmp21-keygen](https://docs.rs/cggmp21-keygen/0.5.0/cggmp21_keygen/) --- it's +[cggmp21-keygen](https://docs.rs/cggmp21-keygen/0.5.0/cggmp21_keygen/) - it's an interactive protocol built on the same foundations of `round-based` and -`generic-ec`. +`generic-ec`, and outputs the key share of the same type and format as expected +by this library. ### Networking @@ -83,7 +84,7 @@ using its primitives, which are in practice the `Sink` and `Stream` types from The exact underlying mechanism behind the sink and stream depend on the application and are not provided here. Some application will use libp2p, others may want to use a message broker like kafka or postgres. -No matter the implementation, all messages need to be authenticated --- when +No matter the implementation, all messages need to be authenticated - when one party receives a message from another, it needs to be able to verify that the message comes from the claimed sender. diff --git a/src/lib.rs b/src/lib.rs index 5760641..0be6298 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,6 +92,9 @@ pub enum AggregateFailed { #[cfg(test)] mod test { + // Used in doctests only + use futures as _; + type E = generic_ec::curves::Secp256k1; #[test_case::test_case(3, 5; "t3n5")]