Skip to content

Commit

Permalink
Add more docs
Browse files Browse the repository at this point in the history
  • Loading branch information
survived committed Mar 19, 2024
1 parent b59a333 commit 33fb48b
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 8 deletions.
136 changes: 133 additions & 3 deletions givre/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,144 @@
//! This crate doesn't support (currently):
//! * Identifiable abort
//!
//! The crate is wasm and no_std friendly.
//!
//! # How to use the library
//!
//! ## Distributed Key Generation (DKG)
//! First of all, you need to generate a key. For that purpose, you can use any secure
//! (preferrably, UC-secure) DKG protocol. FROST IETF Draft does not define any DKG
//! protocol or requirements it needs to meet, so the choice is up to you. This library
//! re-exports CGGMP21 DKG from [`cggmp21-keygen`] crate which is proven to be UC-secure
//! and should be a reasonable default.
//! re-exports CGGMP21 DKG from [`cggmp21-keygen`] crate when `cggmp21-keygen` feature
//! is enabled which is proven to be UC-secure and should be a reasonable default.
//!
//! CGGMP21 DKG is an interactive protocol built on [`round_based`] framework. In order
//! to carry it out, you need to define the transport layer (i.e. how the signers can
//! communicate with each other). It's simply a pair of stream and sink:
//!
//! ```rust,ignore
//! let incoming: impl Stream<Item = Result<Incoming<Msg>>>;
//! let outgoing: impl Sink<Outgoing<Msg>>;
//! ```
//!
//! where:
//! * `Msg` is a protocol message (e.g., [`keygen::msg::threshold::Msg`])
//! * [`round_based::Incoming`] and [`round_based::Outgoing`] wrap `Msg` and provide additional data (e.g., sender/recepient)
//! * [`futures::Stream`] and [`futures::Sink`] are well-known async primitives.
//!
//! Transport layer implementation needs to meet requirements:
//! * All messages must be authenticated \
//! Whenever one party receives a message from another, the receiver should cryptographically
//! verify that the message comes from the claimed sender.
//! * All p2p messages must be encrypted \
//! Only the designated recipient should be able to read the message
//!
//! Then, construct an [MpcParty](round_based::MpcParty):
//! ```rust
//! # type Msg = givre::keygen::msg::threshold::Msg<givre::generic_ec::curves::Secp256k1, givre::keygen::security_level::SecurityLevel128, sha2::Sha256>;
//! # let incoming = futures::stream::pending::<Result<round_based::Incoming<Msg>, std::convert::Infallible>>();
//! # let outgoing = futures::sink::drain::<round_based::Outgoing<Msg>>();
//! let delivery = (incoming, outgoing);
//! let party = round_based::MpcParty::connected(delivery);
//! ```
//!
//! Now, you can finally execute the DKG protocol. The protocol involves all signers
//! who will co-share a key. All signers need to agree on some basic parameters including
//! the participants’ indices, the execution ID, and the threshold value (i.e., t).
//! ```rust,no_run
//! use givre::ciphersuite::{Ciphersuite, Secp256k1};
//!
//! # async fn doc() -> Result<(), givre::keygen::KeygenError> {
//! # type Msg = givre::keygen::msg::threshold::Msg<<Secp256k1 as Ciphersuite>::Curve, givre::keygen::security_level::SecurityLevel128, sha2::Sha256>;
//! # let incoming = futures::stream::pending::<Result<round_based::Incoming<Msg>, std::convert::Infallible>>();
//! # let outgoing = futures::sink::drain::<round_based::Outgoing<Msg>>();
//! # let delivery = (incoming, outgoing);
//! # let party = round_based::MpcParty::connected(delivery);
//! #
//! # use rand_core::OsRng;
//! #
//! let eid = givre::keygen::ExecutionId::new(b"execution id, unique per protocol execution");
//! let i = /* signer index (0 <= i < n) */
//! # 0;
//! let n = /* number of signers taking part in key generation */
//! # 3;
//! let t = /* threshold */
//! # 2;
//!
//! let key_share = givre::keygen::<<Secp256k1 as Ciphersuite>::Curve>(eid, i, n)
//! .set_threshold(t)
//! .start(&mut OsRng, party)
//! .await?;
//! # Ok(()) }
//! ```
//!
//! ## Signing
//! FROST signing can be carried out either interactively with the help of [`round_based`]
//! framework, or manually.
//!
//! ### Manual Signing
//! In the manual signing, as the name suggests, you manually construct all messages
//! and drive the protocol. It gives you better control over protocol execution and
//! you can benefit from better performance (e.g. by having 1 round signing). However,
//! it also gives a greater chance of misusing the protocol and violating security.
//! When opting for manual signing, make sure you're familiar with the [FROST IETF Draft][draft].
//! Refer to [mod@signing] module docs for the instructions.
//!
//! ### Interactive Signing (requires `full-signing` feature)
//! Interactive Signing has more user-friendly interface and harder-to-misuse design.
//! It works on top of [`round_based`] framework similarly to DKG described above.
//! As before, you need to define a secure transport layer and construct [MpcParty](round_based::MpcParty).
//! Then, you need to assign each signer a unique index, in range from 0 to t-1. The
//! signers also need to know which index each of them occupied at the time of keygen.
//!
//! ```rust,no_run
//! use givre::ciphersuite::Secp256k1;
//!
//! # async fn doc() -> Result<(), givre::signing::full_signing::FullSigningError> {
//! # type Msg = givre::signing::full_signing::Msg<<Secp256k1 as givre::Ciphersuite>::Curve>;
//! # let incoming = futures::stream::pending::<Result<round_based::Incoming<Msg>, std::convert::Infallible>>();
//! # let outgoing = futures::sink::drain::<round_based::Outgoing<Msg>>();
//! # let delivery = (incoming, outgoing);
//! # let party = round_based::MpcParty::connected(delivery);
//! #
//! # use rand_core::OsRng;
//! # const MIN_SIGNERS: usize = 3;
//! #
//! #
//! let i = /* signer index (0 <= i < min_signers) */
//! # 0;
//! let parties_indexes_at_keygen: [u16; MIN_SIGNERS] =
//! /* parties_indexes_at_keygen[i] is the index the i-th party had at keygen */
//! # [0, 1, 2];
//! let key_share = /* key share */
//! # {let s: givre::KeyShare<<Secp256k1 as givre::Ciphersuite>::Curve> = unimplemented!(); s};
//!
//! let data_to_sign = b"data to be signed";
//!
//! let signature = givre::signing::<Secp256k1>(i, &key_share, &parties_indexes_at_keygen, data_to_sign)
//! .sign(&mut OsRng, party)
//! .await?;
//! # Ok(()) }
//! ```
//! ## Signer indices
//! 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.
//!
//! 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.
//!
//! # Webassembly and `no_std` support
//! This crate is compatible with `wasm32-unknown-unknown` target and `no_std` unless
//! `cggmp21-keygen` or `full-signing` features are enabled. Other WASM targets might
//! be supported even if these features are on.
//!
//! [CGGMP21]: https://github.com/dfns/cggmp21
//! [draft]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-15.html
Expand Down Expand Up @@ -150,7 +280,7 @@ pub type SignerIndex = u16;
///
/// let party = round_based::MpcParty::connected(delivery);
/// let sig = givre::signing::<Secp256k1>(i, &key_share, &signers, msg)
/// .sign(party, &mut rand_core::OsRng)
/// .sign(&mut rand_core::OsRng, party)
/// .await?;
/// # Ok(()) }
/// ```
Expand Down
4 changes: 2 additions & 2 deletions givre/src/signing/full_signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ impl<'a, C: Ciphersuite> SigningBuilder<'a, C> {
/// them.
pub async fn issue_sig_share<M, R>(
self,
party: M,
rng: &mut R,
party: M,
) -> Result<SigShare<C::Curve>, FullSigningError>
where
M: round_based::Mpc<ProtocolMessage = Msg<C::Curve>>,
Expand All @@ -84,7 +84,7 @@ impl<'a, C: Ciphersuite> SigningBuilder<'a, C> {
}

/// Executes Interactive Signing protocol
pub async fn sign<M, R>(self, party: M, rng: &mut R) -> Result<Signature<C>, FullSigningError>
pub async fn sign<M, R>(self, rng: &mut R, party: M) -> Result<Signature<C>, FullSigningError>
where
M: round_based::Mpc<ProtocolMessage = Msg<C::Curve>>,
R: RngCore + CryptoRng,
Expand Down
2 changes: 1 addition & 1 deletion tests/tests/it/interactive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ mod generic {
.zip(iter::repeat_with(|| (rng.fork(), simulation.add_party())))
.map(|((j, &index_at_keygen), (mut rng, party))| async move {
givre::signing::<C>(j, &key_shares[usize::from(index_at_keygen)], signers, msg)
.sign(party, &mut rng)
.sign(&mut rng, party)
.await
});

Expand Down
46 changes: 45 additions & 1 deletion wasm/no_std/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 wasm/no_std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
givre = { path = "../../givre", default-features = false, features = ["all-ciphersuites"] }
givre = { path = "../../givre", default-features = false, features = ["all-ciphersuites", "spof"] }

[patch.crates-io.key-share]
git = "https://github.com/dfns/cggmp21"
Expand Down

0 comments on commit 33fb48b

Please sign in to comment.