From 5a86e0c1e2e383bd9290d4a05d67bae7ca16fef8 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Sun, 2 May 2021 12:31:46 -0300 Subject: [PATCH 01/70] add a messages rfc --- rfcs/0001-messages.md | 146 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 rfcs/0001-messages.md diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md new file mode 100644 index 00000000..f0f2c19b --- /dev/null +++ b/rfcs/0001-messages.md @@ -0,0 +1,146 @@ +# FROST messages + +Proposes a message layout to exchange information between participants of a FROST setup. + +## Motivation + +Currently FROST library is complete for 2 round signatures with a dealer/aggregator setup. +This proposal is only considering that specific features, additions and upgrades will need to be made when DKG is implemented. + +Assuming all participants have a FROST library available we need to define message structures in a way that data can be exchanged between participants. The proposal is a collection of data types so each side can do all the actions needed for a real life situation. + +## Definitions + +- `dealer` +- `aggergator` +- `signer` +- `nonce` +- `commitment` +- + +## Guide-level explanation + +We propose a message separated in 2 parts, a header and a payload: + +```rust +struct Message { + header: Header, + payload: Payload, +} +``` + +`Header` will look as follows: + +```rust +struct Header { + msg_type: MsgType, + version: MsgVersion, + sender: Participant, + receiver: Participant, +} +``` + +While `Payload` will be defined as: + +```rust +enum Payload { + DealerBroadcast(MsgDealerBroadcast), + Commitments(MsgCommitments), + SigningPackage(MsgSigningPackage), + SignatureShare(MsgSignatureShare), + FinalSignature(MsgFinalSignature), +} +``` + +All the messages and new types will be defined in a new file `src/frost/messages.rs` + +## Reference-level explanation + +Here we explore in detail the header types and all the message payloads. + +### Header + +Fields of the header define new types. Proposed implementation for them is as follows: + +```rust +enum MsgType { + DealerBroadcast = 1, + Commitments = 2, + SigningPackage = 3, + SignatureShare = 4, + FinalSignature = 5, +} + +enum MsgVersion { + FirstVersion = 1, + .. +} + +struct Participant(u8); +``` + +### Payloads + +Each payload defines a new message: + +```rust +// The dealer broadcast initial data, dealer must send +// this message to each participant involved. +struct MsgDealerBroadcast { + share: frost::SharePackage, +} + +// The signer participants sends the commitments to the +// aggregator. +struct MsgCommitments { + commitment: frost::SigningCommitments, +} + +// The aggergator decide what message to be signed, +// prepare it and broadcast to signers. +struct MsgSigningPackage { + signing_package: frost::SigningPackage, +} + +// Each signer send the signatures to the agregator who is going to collect them +// and generate a final spend signature +struct MsgSignatureShare { + signature_share: frost::SignatureShare, +} + +// The final signature is broadcasted by the aggegator +// to any participant. +struct MsgFinalSignature { + final_signature: Signature, +} +``` +## Serialization/Deserialization + +Each message struct needs to serialize to bytes representation before it is sent through the wire and must deserialize to the same struct (round trip) on the receiver side. We use `serde` and macro derivations (`Serialize` and `Deserialize`) to automatically implement where possible. + +This will require deriving serde in several types defined in `frost.rs`. +Manual implementation of serialization/deserialization will be located at a new mod `src/frost/serialize.rs`. + +FROST inherit types from `jubjub` such as `Scalar`, `ExtendedPoint`, `AffinePoint`, etc. We need to decide how serialization of these types that are defined in external crates will be done (maybe with wrappers?). + +## Validation + +Validation is implemented to each new data type as needed. This will ensure the creation of valid messages before they are sent. For example, in the header sender and receiver can't be the same: + +```rust +impl Header { + pub fn validate(&self) { + if self.sender.0 == self.receiver.0 { + panic!("sender and receiver are the same"); + } + } +} +``` + +## Testing plan + +- Create a happy path unit test similar to https://github.com/ZcashFoundation/redjubjub/blob/frost-messages/tests/frost.rs#L7 and: + - Make messages on each step. + - Simulate send/receive. + - Test round trip serialization/deserialization on each message. +- Create property tests for each message. From c8eda8147ff42b184d36453db22aff8ff3df41b2 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 4 May 2021 11:55:14 -0300 Subject: [PATCH 02/70] update MsgDealerBroadcast --- rfcs/0001-messages.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index f0f2c19b..3d8f85c6 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -84,10 +84,19 @@ struct Participant(u8); Each payload defines a new message: ```rust -// The dealer broadcast initial data, dealer must send -// this message to each participant involved. +// Dealer must send this message with initial data to each participant involved. +// With this, the participant should be able to build a `SharePackage` and use +// the `sign()` function. struct MsgDealerBroadcast { - share: frost::SharePackage, + // The secret key as a frost::Scalar. + secret_key: frost::Scalar, + // Set of commitments as jubjub::ExtendedPoint using frost::Commitment wrapper. + commitment: Vec, + // The public key as jubjub::ExtendedPoint using frost::Public wrapper. + public_key: frost::Public, + // The public signing key that represents the entire group. + // This is a jubjub::ExtendedPoint and verification bytes. + group_public: VerificationKey, } // The signer participants sends the commitments to the From d0a569cd55e4014c16f79bbc4e70c63922398032 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 4 May 2021 12:19:26 -0300 Subject: [PATCH 03/70] remove msg type numbers, fix version msg --- rfcs/0001-messages.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 3d8f85c6..cc0ee7de 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -63,18 +63,17 @@ Here we explore in detail the header types and all the message payloads. Fields of the header define new types. Proposed implementation for them is as follows: ```rust +#[repr(u8)] +#[non_exhaustive] enum MsgType { - DealerBroadcast = 1, - Commitments = 2, - SigningPackage = 3, - SignatureShare = 4, - FinalSignature = 5, + DealerBroadcast, + Commitments, + SigningPackage, + SignatureShare, + FinalSignature, } -enum MsgVersion { - FirstVersion = 1, - .. -} +struct MsgVersion(u8); struct Participant(u8); ``` From 0e4a911c16b4627f50e8f120696b50769e750477 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 4 May 2021 12:40:42 -0300 Subject: [PATCH 04/70] update MsgCommitments --- rfcs/0001-messages.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index cc0ee7de..38019dbb 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -98,10 +98,13 @@ struct MsgDealerBroadcast { group_public: VerificationKey, } -// The signer participants sends the commitments to the -// aggregator. +// Each signer participant send to the aggregator the 2 points +// needed for commitment building. struct MsgCommitments { - commitment: frost::SigningCommitments, + // The hiding Point. + hiding: jubjub::ExtendedPoint, + // The binding Point. + binding: jubjub::ExtendedPoint, } // The aggergator decide what message to be signed, From 994a67a4d150860afc2d61f9d2fd6f38379991d5 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 4 May 2021 13:01:02 -0300 Subject: [PATCH 05/70] update MsgSigningPackage and MsgSignatureShare --- rfcs/0001-messages.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 38019dbb..074fb7bc 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -107,16 +107,19 @@ struct MsgCommitments { binding: jubjub::ExtendedPoint, } -// The aggergator decide what message to be signed, -// prepare it and broadcast to signers. +// The aggergator decide what message is going to be signed and +// send it to each participant with all the commitments collected. struct MsgSigningPackage { + // Consist of a [u8] and vector of commitments. + // Commitments are a signer id and a hiding and binding point. signing_package: frost::SigningPackage, } // Each signer send the signatures to the agregator who is going to collect them -// and generate a final spend signature +// and generate a final spend signature. struct MsgSignatureShare { - signature_share: frost::SignatureShare, + // The signature to be shared as a Scalar + signature: frost::Scalar, } // The final signature is broadcasted by the aggegator From e4ecef9ebb37da051779d87bad5cbb198d8084ed Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 4 May 2021 13:38:20 -0300 Subject: [PATCH 06/70] remove non needed fields from MsgDealerBroadcast --- rfcs/0001-messages.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 074fb7bc..bdac4f80 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -86,16 +86,13 @@ Each payload defines a new message: // Dealer must send this message with initial data to each participant involved. // With this, the participant should be able to build a `SharePackage` and use // the `sign()` function. +// `group_public` is random data at this stage and `public_key` can be calculated +// from `secret_key`. struct MsgDealerBroadcast { // The secret key as a frost::Scalar. secret_key: frost::Scalar, // Set of commitments as jubjub::ExtendedPoint using frost::Commitment wrapper. commitment: Vec, - // The public key as jubjub::ExtendedPoint using frost::Public wrapper. - public_key: frost::Public, - // The public signing key that represents the entire group. - // This is a jubjub::ExtendedPoint and verification bytes. - group_public: VerificationKey, } // Each signer participant send to the aggregator the 2 points From 4cf6b87a9ed6c11797e784cabc38627180ea3dc4 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 4 May 2021 14:48:22 -0300 Subject: [PATCH 07/70] update validate section --- rfcs/0001-messages.md | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index bdac4f80..a6dd7766 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -136,18 +136,35 @@ FROST inherit types from `jubjub` such as `Scalar`, `ExtendedPoint`, `AffinePoin ## Validation -Validation is implemented to each new data type as needed. This will ensure the creation of valid messages before they are sent. For example, in the header sender and receiver can't be the same: +Validation is implemented to each new data type as needed. This will ensure the creation of valid messages before they are sent. We create a trait for this as follows: ```rust -impl Header { - pub fn validate(&self) { +pub trait Validate { + fn validate(&self) -> &Self; +} +``` + +And we implement where needed. For example, in the header sender and receiver can't be the same: + +```rust +impl Validate for Header { + fn validate(&self) -> &Self { if self.sender.0 == self.receiver.0 { panic!("sender and receiver are the same"); } + self } } ``` +Then to create a valid `Header` we call: + +```rust +let header = Validate::validate(&Header { + .. +}).clone(); +``` + ## Testing plan - Create a happy path unit test similar to https://github.com/ZcashFoundation/redjubjub/blob/frost-messages/tests/frost.rs#L7 and: From 4b88806d947007dab221bea668bb1cf0ebb5b132 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 4 May 2021 14:52:39 -0300 Subject: [PATCH 08/70] include the receiver side in validation section --- rfcs/0001-messages.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index a6dd7766..43134341 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -136,7 +136,7 @@ FROST inherit types from `jubjub` such as `Scalar`, `ExtendedPoint`, `AffinePoin ## Validation -Validation is implemented to each new data type as needed. This will ensure the creation of valid messages before they are sent. We create a trait for this as follows: +Validation is implemented to each new data type as needed. This will ensure the creation of valid messages before they are sent and right after they are received. We create a trait for this as follows: ```rust pub trait Validate { @@ -165,6 +165,12 @@ let header = Validate::validate(&Header { }).clone(); ``` +The receiver side will validate the header as: + +```rust +msg.header.validate(); +``` + ## Testing plan - Create a happy path unit test similar to https://github.com/ZcashFoundation/redjubjub/blob/frost-messages/tests/frost.rs#L7 and: From 3d54c96d02c4c965c354b14d649f23b570769dea Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 4 May 2021 17:14:24 -0300 Subject: [PATCH 09/70] start a Serialized Size section --- rfcs/0001-messages.md | 53 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 43134341..080039a5 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -92,7 +92,7 @@ struct MsgDealerBroadcast { // The secret key as a frost::Scalar. secret_key: frost::Scalar, // Set of commitments as jubjub::ExtendedPoint using frost::Commitment wrapper. - commitment: Vec, + commitments: Vec, } // Each signer participant send to the aggregator the 2 points @@ -171,6 +171,57 @@ The receiver side will validate the header as: msg.header.validate(); ``` +## Serialized Size + +### Header + +The `Header` part of the message is 4 bytes total: + +Bytes | Field name | Data type +------|------------|----------- +1 | msg_type | u8 +1 | version | u8 +1 | sender | u8 +1 | receiver | u8 + +## Primitive types + +`Payload`s use data types that we need to specify first. We have 3 primitive types inside the payload messages: + +`Scalar` + +`Scalar` is a better name for `jubjub::Fr` and this is a `[u64; 4]` as documented in https://github.com/zkcrypto/jubjub/blob/main/src/fr.rs#L16 + +`Commitment` + +`Commitment` is a wrapper of `jubjub::ExtendedPoint` and this is a structure with 5 `jubjub::Fq`s as defined in https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L128-L134 + +Each `Fq` needed to form a `jubjub::ExtendedPoint` are `Scalar`s of `bls12_381` crate. Scalar here is `[u64; 4]` as documented in https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L16 + +`ExtendedPoint` + +`ExtendedPoint` was detailed above, it is 5 `[u64; 4]`. The total size of an `ExtendedPoint` is 1280 bytes. + +## Payload + +Payload part of the message is variable in size and depends on message type. + +`MsgDealerBroadcast`: + +Bytes | Field name | Data type +------|------------|----------- +256 | secret_key | Scalar +1280*n| commitments| [Commitment; n] + +`MsgCommitments`: + +`MsgSigningPackage`: + +`SignatureShare`: + +`MsgFinalSignature`: + + ## Testing plan - Create a happy path unit test similar to https://github.com/ZcashFoundation/redjubjub/blob/frost-messages/tests/frost.rs#L7 and: From f74f57ca08a7b7317164ad5ee5eb79df6af7c4e7 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 4 May 2021 17:26:25 -0300 Subject: [PATCH 10/70] complete the rest of the messages sizes --- rfcs/0001-messages.md | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 080039a5..3a64384e 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -188,17 +188,17 @@ Bytes | Field name | Data type `Payload`s use data types that we need to specify first. We have 3 primitive types inside the payload messages: -`Scalar` +**`Scalar`** `Scalar` is a better name for `jubjub::Fr` and this is a `[u64; 4]` as documented in https://github.com/zkcrypto/jubjub/blob/main/src/fr.rs#L16 -`Commitment` +**`Commitment`** `Commitment` is a wrapper of `jubjub::ExtendedPoint` and this is a structure with 5 `jubjub::Fq`s as defined in https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L128-L134 Each `Fq` needed to form a `jubjub::ExtendedPoint` are `Scalar`s of `bls12_381` crate. Scalar here is `[u64; 4]` as documented in https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L16 -`ExtendedPoint` +**`ExtendedPoint`** `ExtendedPoint` was detailed above, it is 5 `[u64; 4]`. The total size of an `ExtendedPoint` is 1280 bytes. @@ -206,20 +206,38 @@ Each `Fq` needed to form a `jubjub::ExtendedPoint` are `Scalar`s of `bls12_381` Payload part of the message is variable in size and depends on message type. -`MsgDealerBroadcast`: +**`MsgDealerBroadcast`** Bytes | Field name | Data type ------|------------|----------- 256 | secret_key | Scalar 1280*n| commitments| [Commitment; n] -`MsgCommitments`: +**`MsgCommitments`** -`MsgSigningPackage`: +Bytes | Field name | Data type +------|------------|----------- +1280 | hiding | ExtendedPoint +1280 | binding | ExtendedPoint + +**`MsgSigningPackage`** -`SignatureShare`: +Bytes | Field name | Data type +-----------|----------------|----------- +1+(1280*n) | signing_package| u8 [Commitment; n] -`MsgFinalSignature`: + +**`SignatureShare`** + +Bytes | Field name | Data type +------|------------|----------- +256 | signature | Scalar + +**`MsgFinalSignature`** + +Bytes | Field name | Data type +------|------------|----------- +64 | signature | [u8; 32] [u8; 32] ## Testing plan From b725e224fa34a26a34b1c6a3946dc7206f76a8e1 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Wed, 5 May 2021 13:00:43 -0300 Subject: [PATCH 11/70] join serialization sections, readd `group_public` to initial msg --- rfcs/0001-messages.md | 61 +++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 3a64384e..dce085ad 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -93,6 +93,8 @@ struct MsgDealerBroadcast { secret_key: frost::Scalar, // Set of commitments as jubjub::ExtendedPoint using frost::Commitment wrapper. commitments: Vec, + // The generated public key for the group. + group_public: VerificationKey, } // Each signer participant send to the aggregator the 2 points @@ -125,14 +127,6 @@ struct MsgFinalSignature { final_signature: Signature, } ``` -## Serialization/Deserialization - -Each message struct needs to serialize to bytes representation before it is sent through the wire and must deserialize to the same struct (round trip) on the receiver side. We use `serde` and macro derivations (`Serialize` and `Deserialize`) to automatically implement where possible. - -This will require deriving serde in several types defined in `frost.rs`. -Manual implementation of serialization/deserialization will be located at a new mod `src/frost/serialize.rs`. - -FROST inherit types from `jubjub` such as `Scalar`, `ExtendedPoint`, `AffinePoint`, etc. We need to decide how serialization of these types that are defined in external crates will be done (maybe with wrappers?). ## Validation @@ -171,7 +165,12 @@ The receiver side will validate the header as: msg.header.validate(); ``` -## Serialized Size +## Serialization/Deserialization + +Each message struct needs to serialize to bytes representation before it is sent through the wire and must deserialize to the same struct (round trip) on the receiver side. We use `serde` and macro derivations (`Serialize` and `Deserialize`) to automatically implement where possible. + +This will require deriving serde in several types defined in `frost.rs`. +Manual implementation of serialization/deserialization will be located at a new mod `src/frost/serialize.rs`. ### Header @@ -184,60 +183,72 @@ Bytes | Field name | Data type 1 | sender | u8 1 | receiver | u8 -## Primitive types +### Primitive types `Payload`s use data types that we need to specify first. We have 3 primitive types inside the payload messages: -**`Scalar`** +#### `Scalar` `Scalar` is a better name for `jubjub::Fr` and this is a `[u64; 4]` as documented in https://github.com/zkcrypto/jubjub/blob/main/src/fr.rs#L16 -**`Commitment`** +#### `Commitment` `Commitment` is a wrapper of `jubjub::ExtendedPoint` and this is a structure with 5 `jubjub::Fq`s as defined in https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L128-L134 Each `Fq` needed to form a `jubjub::ExtendedPoint` are `Scalar`s of `bls12_381` crate. Scalar here is `[u64; 4]` as documented in https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L16 -**`ExtendedPoint`** +#### `ExtendedPoint` `ExtendedPoint` was detailed above, it is 5 `[u64; 4]`. The total size of an `ExtendedPoint` is 1280 bytes. -## Payload +### FROST types + +`Payload`s also use some types that are defined in the `redjubjub` crate. Here we describe them from a serialization point of view. + +#### `VerificationKey` + +Defined in `verification_key.rs` it consist of 1 `ExtendedPoint` and 1 `VerificationKeyBytes` which is also defined in the same file and consist of 1 `[u8; 32]`. + +#### `Signature` + +Defined in `signature.rs` consist of 2 `[u8; 32]` arrays. + +### Payload Payload part of the message is variable in size and depends on message type. -**`MsgDealerBroadcast`** +#### `MsgDealerBroadcast` -Bytes | Field name | Data type -------|------------|----------- -256 | secret_key | Scalar -1280*n| commitments| [Commitment; n] +Bytes | Field name | Data type +-------|-------------|----------- +256 | secret_key | Scalar +1280*n | commitments | [Commitment; n] +1280+32| group_public| VerificationKey -**`MsgCommitments`** +#### `MsgCommitments` Bytes | Field name | Data type ------|------------|----------- 1280 | hiding | ExtendedPoint 1280 | binding | ExtendedPoint -**`MsgSigningPackage`** +#### `MsgSigningPackage` Bytes | Field name | Data type -----------|----------------|----------- 1+(1280*n) | signing_package| u8 [Commitment; n] - -**`SignatureShare`** +#### `SignatureShare` Bytes | Field name | Data type ------|------------|----------- 256 | signature | Scalar -**`MsgFinalSignature`** +#### `MsgFinalSignature` Bytes | Field name | Data type ------|------------|----------- -64 | signature | [u8; 32] [u8; 32] +64 | signature | Signature ## Testing plan From b80a5a6aca80c4d7c5ca0a2bf9085af040cc0cf6 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Wed, 5 May 2021 20:57:13 -0300 Subject: [PATCH 12/70] add frost:: to frost types, update intro, others --- rfcs/0001-messages.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index dce085ad..054b530d 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -1,6 +1,6 @@ # FROST messages -Proposes a message layout to exchange information between participants of a FROST setup. +Proposes a message layout to exchange information between participants of a FROST setup using the [jubjub](https://github.com/zkcrypto/jubjub) curve. ## Motivation @@ -86,7 +86,7 @@ Each payload defines a new message: // Dealer must send this message with initial data to each participant involved. // With this, the participant should be able to build a `SharePackage` and use // the `sign()` function. -// `group_public` is random data at this stage and `public_key` can be calculated +// `public_key` can be calculated from the `secret_key`. // from `secret_key`. struct MsgDealerBroadcast { // The secret key as a frost::Scalar. @@ -94,7 +94,7 @@ struct MsgDealerBroadcast { // Set of commitments as jubjub::ExtendedPoint using frost::Commitment wrapper. commitments: Vec, // The generated public key for the group. - group_public: VerificationKey, + group_public: frost::VerificationKey, } // Each signer participant send to the aggregator the 2 points @@ -124,13 +124,13 @@ struct MsgSignatureShare { // The final signature is broadcasted by the aggegator // to any participant. struct MsgFinalSignature { - final_signature: Signature, + final_signature: frost::Signature, } ``` ## Validation -Validation is implemented to each new data type as needed. This will ensure the creation of valid messages before they are sent and right after they are received. We create a trait for this as follows: +Validation is implemented to each new data type as needed. This will ensure the creation of valid messages before they are send and right after they are received. We create a trait for this as follows: ```rust pub trait Validate { @@ -138,7 +138,7 @@ pub trait Validate { } ``` -And we implement where needed. For example, in the header sender and receiver can't be the same: +And we implement where needed. For example, in the header, sender and receiver can't be the same: ```rust impl Validate for Header { From dd0b7deb8b1d2f7445020dc3fe66c53936818148 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Wed, 5 May 2021 21:07:13 -0300 Subject: [PATCH 13/70] change better name for alias --- rfcs/0001-messages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 054b530d..7b03c012 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -189,7 +189,7 @@ Bytes | Field name | Data type #### `Scalar` -`Scalar` is a better name for `jubjub::Fr` and this is a `[u64; 4]` as documented in https://github.com/zkcrypto/jubjub/blob/main/src/fr.rs#L16 +`Scalar` is a an alias for `jubjub::Fr` and this is a `[u64; 4]` as documented in https://github.com/zkcrypto/jubjub/blob/main/src/fr.rs#L16 #### `Commitment` From d0c8f18153b716c016391cd65416ec5c87244572 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 6 May 2021 10:20:06 -0300 Subject: [PATCH 14/70] unpack SigningPackage in MsgSigningPackage --- rfcs/0001-messages.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 7b03c012..3e305b51 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -109,9 +109,10 @@ struct MsgCommitments { // The aggergator decide what message is going to be signed and // send it to each participant with all the commitments collected. struct MsgSigningPackage { - // Consist of a [u8] and vector of commitments. - // Commitments are a signer id and a hiding and binding point. - signing_package: frost::SigningPackage, + // The message to be signed as bytes + message: &'static [u8], + // The collected unpacked commitments for each signer + commitments: Vec<(u8, jubjub::ExtendedPoint, jubjub::ExtendedPoint), } // Each signer send the signatures to the agregator who is going to collect them From b3902e513f727fdd3ce3c91ba0dfb2e462247365 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 6 May 2021 10:36:48 -0300 Subject: [PATCH 15/70] unpack commitments in MsgDealerBroadcast --- rfcs/0001-messages.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 3e305b51..6cf4972e 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -91,8 +91,8 @@ Each payload defines a new message: struct MsgDealerBroadcast { // The secret key as a frost::Scalar. secret_key: frost::Scalar, - // Set of commitments as jubjub::ExtendedPoint using frost::Commitment wrapper. - commitments: Vec, + // Commitments for the signer as jubjub::ExtendedPoint. + commitment: jubjub::ExtendedPoint, // The generated public key for the group. group_public: frost::VerificationKey, } From 647d19967dc00593a90778f2a87ab867dd85c05a Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 6 May 2021 11:01:41 -0300 Subject: [PATCH 16/70] use affinepoint everywhere --- rfcs/0001-messages.md | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 6cf4972e..c4494693 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -91,8 +91,8 @@ Each payload defines a new message: struct MsgDealerBroadcast { // The secret key as a frost::Scalar. secret_key: frost::Scalar, - // Commitments for the signer as jubjub::ExtendedPoint. - commitment: jubjub::ExtendedPoint, + // Commitments for the signer as jubjub::AffinePoint. + commitment: jubjub::AffinePoint, // The generated public key for the group. group_public: frost::VerificationKey, } @@ -101,9 +101,9 @@ struct MsgDealerBroadcast { // needed for commitment building. struct MsgCommitments { // The hiding Point. - hiding: jubjub::ExtendedPoint, + hiding: jubjub::AffinePoint, // The binding Point. - binding: jubjub::ExtendedPoint, + binding: jubjub::AffinePoint, } // The aggergator decide what message is going to be signed and @@ -112,7 +112,7 @@ struct MsgSigningPackage { // The message to be signed as bytes message: &'static [u8], // The collected unpacked commitments for each signer - commitments: Vec<(u8, jubjub::ExtendedPoint, jubjub::ExtendedPoint), + commitments: Vec<(u8, jubjub::AffinePoint, jubjub::AffinePoint), } // Each signer send the signatures to the agregator who is going to collect them @@ -186,21 +186,24 @@ Bytes | Field name | Data type ### Primitive types -`Payload`s use data types that we need to specify first. We have 3 primitive types inside the payload messages: +`Payload`s use data types that we need to specify first. We have 2 primitive types inside the payload messages: #### `Scalar` `Scalar` is a an alias for `jubjub::Fr` and this is a `[u64; 4]` as documented in https://github.com/zkcrypto/jubjub/blob/main/src/fr.rs#L16 -#### `Commitment` +#### `AffinePoint` -`Commitment` is a wrapper of `jubjub::ExtendedPoint` and this is a structure with 5 `jubjub::Fq`s as defined in https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L128-L134 +Much of the math in FROST is done using `jubjub::ExtendedPoint`. This is a structure with 5 `jubjub::Fq`s as defined in https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L128-L134 Each `Fq` needed to form a `jubjub::ExtendedPoint` are `Scalar`s of `bls12_381` crate. Scalar here is `[u64; 4]` as documented in https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L16 -#### `ExtendedPoint` +For message exchange `jubjub::AffinePoint`s are a better choice as they are shorter in bytes, they are formed of 2 `jubjub::Fq` instead of 5: https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L70-L73 -`ExtendedPoint` was detailed above, it is 5 `[u64; 4]`. The total size of an `ExtendedPoint` is 1280 bytes. +Conversion from one type to the other is trivial: + +https://docs.rs/jubjub/0.6.0/jubjub/struct.AffinePoint.html#impl-From%3CExtendedPoint%3E +https://docs.rs/jubjub/0.6.0/jubjub/struct.ExtendedPoint.html#impl-From%3CAffinePoint%3E ### FROST types @@ -223,21 +226,22 @@ Payload part of the message is variable in size and depends on message type. Bytes | Field name | Data type -------|-------------|----------- 256 | secret_key | Scalar -1280*n | commitments | [Commitment; n] +512 | commitments | AffinePoint 1280+32| group_public| VerificationKey #### `MsgCommitments` Bytes | Field name | Data type ------|------------|----------- -1280 | hiding | ExtendedPoint -1280 | binding | ExtendedPoint +512 | hiding | AffinePoint +512 | binding | AffinePoint #### `MsgSigningPackage` Bytes | Field name | Data type -----------|----------------|----------- -1+(1280*n) | signing_package| u8 [Commitment; n] +? | message | [u8] +1+256+256 | commitments | (u8, AffinePoint, AffinePoint) #### `SignatureShare` From 73944006e53a3b71c92d7bfc11b6aa0ec7090509 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 6 May 2021 11:31:39 -0300 Subject: [PATCH 17/70] unpack VerificationKey --- rfcs/0001-messages.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index c4494693..b5f57db5 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -93,8 +93,8 @@ struct MsgDealerBroadcast { secret_key: frost::Scalar, // Commitments for the signer as jubjub::AffinePoint. commitment: jubjub::AffinePoint, - // The generated public key for the group. - group_public: frost::VerificationKey, + // The point and verification bytes needed to generate the group public key + group_public: (jubjub::AffinePoint, [u8; 32]), } // Each signer participant send to the aggregator the 2 points @@ -209,10 +209,6 @@ https://docs.rs/jubjub/0.6.0/jubjub/struct.ExtendedPoint.html#impl-From%3CAffine `Payload`s also use some types that are defined in the `redjubjub` crate. Here we describe them from a serialization point of view. -#### `VerificationKey` - -Defined in `verification_key.rs` it consist of 1 `ExtendedPoint` and 1 `VerificationKeyBytes` which is also defined in the same file and consist of 1 `[u8; 32]`. - #### `Signature` Defined in `signature.rs` consist of 2 `[u8; 32]` arrays. @@ -227,7 +223,7 @@ Bytes | Field name | Data type -------|-------------|----------- 256 | secret_key | Scalar 512 | commitments | AffinePoint -1280+32| group_public| VerificationKey +256+32 | group_public| (AffinePoint, [u8; 32]) #### `MsgCommitments` From c587e845caa22c3ecef4ae031e4b7d7d11728c17 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 6 May 2021 11:36:42 -0300 Subject: [PATCH 18/70] unpack Signature in MsgFinalSignature --- rfcs/0001-messages.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index b5f57db5..dbbb37b5 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -125,7 +125,8 @@ struct MsgSignatureShare { // The final signature is broadcasted by the aggegator // to any participant. struct MsgFinalSignature { - final_signature: frost::Signature, + // The r_bytes and s_bytes needed to build the frost::Signature + final_signature: ([u8; 32], [u8; 32]), } ``` @@ -205,14 +206,6 @@ Conversion from one type to the other is trivial: https://docs.rs/jubjub/0.6.0/jubjub/struct.AffinePoint.html#impl-From%3CExtendedPoint%3E https://docs.rs/jubjub/0.6.0/jubjub/struct.ExtendedPoint.html#impl-From%3CAffinePoint%3E -### FROST types - -`Payload`s also use some types that are defined in the `redjubjub` crate. Here we describe them from a serialization point of view. - -#### `Signature` - -Defined in `signature.rs` consist of 2 `[u8; 32]` arrays. - ### Payload Payload part of the message is variable in size and depends on message type. @@ -249,7 +242,7 @@ Bytes | Field name | Data type Bytes | Field name | Data type ------|------------|----------- -64 | signature | Signature +64 | signature | ([u8; 32], [u8; 32]) ## Testing plan From f98c5cee60d7990b579bf534b3a5f6253b0ae685 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 6 May 2021 12:07:44 -0300 Subject: [PATCH 19/70] update validation section --- rfcs/0001-messages.md | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index dbbb37b5..3910ea4e 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -136,7 +136,7 @@ Validation is implemented to each new data type as needed. This will ensure the ```rust pub trait Validate { - fn validate(&self) -> &Self; + fn validate(&self) -> Result<&Self, MsgErr>; } ``` @@ -144,27 +144,41 @@ And we implement where needed. For example, in the header, sender and receiver c ```rust impl Validate for Header { - fn validate(&self) -> &Self { + fn validate(&self) -> Result<&Self, MsgErr> { if self.sender.0 == self.receiver.0 { - panic!("sender and receiver are the same"); + return Err(MsgErr::SameSenderAndReceiver); } - self + Ok(self) } } ``` -Then to create a valid `Header` we call: +This will require to have validation error messages as: + +```rust +use thiserror::Error; + +#[derive(Clone, Error, Debug)] +pub enum MsgErr { + #[error("sender and receiver are the same")] + SameSenderAndReceiver, +} +``` + +Then to create a valid `Header` in the sender side we call: ```rust let header = Validate::validate(&Header { .. -}).clone(); +}).expect("a valid header"); ``` -The receiver side will validate the header as: +The receiver side will validate the header using the same method. Instead of panicking the error can be ignored to don't crash and keep waiting for other (potentially valid) messages. ```rust -msg.header.validate(); +if let Ok(header) = msg.header.validate() { + .. +} ``` ## Serialization/Deserialization From 1ee805987c24d83231a5f20aa8313d09a1ccf48f Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 6 May 2021 19:20:58 -0300 Subject: [PATCH 20/70] byte order note --- rfcs/0001-messages.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 3910ea4e..407d99e6 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -188,6 +188,10 @@ Each message struct needs to serialize to bytes representation before it is sent This will require deriving serde in several types defined in `frost.rs`. Manual implementation of serialization/deserialization will be located at a new mod `src/frost/serialize.rs`. +### Byte order + +Each byte chunk specified below is in little-endian order unless is specified otherwise. + ### Header The `Header` part of the message is 4 bytes total: From 61740e297179ad47830045bc109bcb22fd9814d9 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 6 May 2021 19:39:32 -0300 Subject: [PATCH 21/70] add size fields in MsgSigningPackage --- rfcs/0001-messages.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 407d99e6..02dd662c 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -12,7 +12,7 @@ Assuming all participants have a FROST library available we need to define messa ## Definitions - `dealer` -- `aggergator` +- `aggregator` - `signer` - `nonce` - `commitment` @@ -109,10 +109,14 @@ struct MsgCommitments { // The aggergator decide what message is going to be signed and // send it to each participant with all the commitments collected. struct MsgSigningPackage { - // The message to be signed as bytes - message: &'static [u8], + // The number of participants. + participants: u8, // The collected unpacked commitments for each signer commitments: Vec<(u8, jubjub::AffinePoint, jubjub::AffinePoint), + // The lenght of the message + message_length: u64, + // The message to be signed as bytes + message: &'static [u8], } // Each signer send the signatures to the agregator who is going to collect them @@ -245,10 +249,12 @@ Bytes | Field name | Data type #### `MsgSigningPackage` -Bytes | Field name | Data type ------------|----------------|----------- -? | message | [u8] -1+256+256 | commitments | (u8, AffinePoint, AffinePoint) +Bytes | Field name | Data type +-----------------------|----------------|----------- +1 | participants | u8 +(1+256+256)*partipants | commitments | Vec<(u8, AffinePoint, AffinePoint)> +8 | message_length | u64 +message_length | message | [u8] #### `SignatureShare` From b60873268d8c8d1374536092aa1db95ac91aac12 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 6 May 2021 19:43:34 -0300 Subject: [PATCH 22/70] fix closing vector --- rfcs/0001-messages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 02dd662c..393da85f 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -112,7 +112,7 @@ struct MsgSigningPackage { // The number of participants. participants: u8, // The collected unpacked commitments for each signer - commitments: Vec<(u8, jubjub::AffinePoint, jubjub::AffinePoint), + commitments: Vec<(u8, jubjub::AffinePoint, jubjub::AffinePoint)>, // The lenght of the message message_length: u64, // The message to be signed as bytes From dc902355a482f72220dae24b63454c9f146bc94a Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Sat, 8 May 2021 20:43:36 -0300 Subject: [PATCH 23/70] replace tuples with new types --- rfcs/0001-messages.md | 61 +++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 393da85f..7a6c3cd7 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -91,15 +91,28 @@ Each payload defines a new message: struct MsgDealerBroadcast { // The secret key as a frost::Scalar. secret_key: frost::Scalar, - // Commitments for the signer as jubjub::AffinePoint. + // Commitment for the signer as a single jubjub::AffinePoint. commitment: jubjub::AffinePoint, - // The point and verification bytes needed to generate the group public key - group_public: (jubjub::AffinePoint, [u8; 32]), + // The public signing key that represents the entire group. + group_public: GroupPublic, +} + +// The point and verification bytes needed to generate the group public key +struct GroupPublic { + // The point + point: jubjub::AffinePoint, + // The verification bytes + bytes: [u8; 32], } // Each signer participant send to the aggregator the 2 points // needed for commitment building. struct MsgCommitments { + commitment: Commitment, +} + +// A commitment specified by two AffinePoints. +struct Commitment { // The hiding Point. hiding: jubjub::AffinePoint, // The binding Point. @@ -111,14 +124,23 @@ struct MsgCommitments { struct MsgSigningPackage { // The number of participants. participants: u8, - // The collected unpacked commitments for each signer - commitments: Vec<(u8, jubjub::AffinePoint, jubjub::AffinePoint)>, + // The collected commitments for each signer + commitments: Vec, // The lenght of the message message_length: u64, // The message to be signed as bytes message: &'static [u8], } +// The aggergator collected commitments for each signer in the +// scheme. +struct CollectedCommitment { + // Signer commitment + signer_id: u8, + // Commitment for this signer + commitment: Commitment, +} + // Each signer send the signatures to the agregator who is going to collect them // and generate a final spend signature. struct MsgSignatureShare { @@ -129,8 +151,16 @@ struct MsgSignatureShare { // The final signature is broadcasted by the aggegator // to any participant. struct MsgFinalSignature { - // The r_bytes and s_bytes needed to build the frost::Signature - final_signature: ([u8; 32], [u8; 32]), + // Bytes needed to build the frost::Signature + final_signature: FinalSignature, +} + +// Final RedJubJub signature the aggergator has created. +struct FinalSignature { + // + r_bytes: [u8; 32], + // + s_bytes: [u8; 32], } ``` @@ -238,21 +268,20 @@ Bytes | Field name | Data type -------|-------------|----------- 256 | secret_key | Scalar 512 | commitments | AffinePoint -256+32 | group_public| (AffinePoint, [u8; 32]) +512+32 | group_public| GroupPublic #### `MsgCommitments` -Bytes | Field name | Data type -------|------------|----------- -512 | hiding | AffinePoint -512 | binding | AffinePoint +Bytes | Field name | Data type +--------|------------|----------- +512+512 | commitment | Commitment #### `MsgSigningPackage` Bytes | Field name | Data type -----------------------|----------------|----------- 1 | participants | u8 -(1+256+256)*partipants | commitments | Vec<(u8, AffinePoint, AffinePoint)> +(1+1024)*partipants | commitments | Vec 8 | message_length | u64 message_length | message | [u8] @@ -264,9 +293,9 @@ Bytes | Field name | Data type #### `MsgFinalSignature` -Bytes | Field name | Data type -------|------------|----------- -64 | signature | ([u8; 32], [u8; 32]) +Bytes | Field name | Data type +------|------------------|----------- +64 | final_signature | FinalSignature ## Testing plan From 104d0f59223e9580fa51141307a4e397eedadf36 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Sun, 9 May 2021 08:36:46 -0300 Subject: [PATCH 24/70] add Other considerations section --- rfcs/0001-messages.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 7a6c3cd7..452b064f 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -297,6 +297,10 @@ Bytes | Field name | Data type ------|------------------|----------- 64 | final_signature | FinalSignature +## Other considerations + +- After the dealer sends the initial `MsgDealerBroadcast` to all the participants, the aggregator must wait for signers to send the second message `MsgCommitments`. There is no timeout for this but only after the aggregator received all the commitments the process can continue. These restrictions and event waiting are not detailed in this RFC. +- This implementation considers not only communications between computer devices in the internet but allows the process to be done by other channels, the lack of timers can result in participants waiting forever for a message. It is the participants business to deal with this and it will not be detailed in this RFC. ## Testing plan From debcbc11d321781043d32cead8bf28113b3d9ad4 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Sun, 9 May 2021 08:48:56 -0300 Subject: [PATCH 25/70] change other considerations section to not included --- rfcs/0001-messages.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 452b064f..e702874d 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -297,10 +297,13 @@ Bytes | Field name | Data type ------|------------------|----------- 64 | final_signature | FinalSignature -## Other considerations +## Not included + +The following are a few things this RFC is not considering: - After the dealer sends the initial `MsgDealerBroadcast` to all the participants, the aggregator must wait for signers to send the second message `MsgCommitments`. There is no timeout for this but only after the aggregator received all the commitments the process can continue. These restrictions and event waiting are not detailed in this RFC. -- This implementation considers not only communications between computer devices in the internet but allows the process to be done by other channels, the lack of timers can result in participants waiting forever for a message. It is the participants business to deal with this and it will not be detailed in this RFC. +- This implementation considers not only communications between computer devices in the internet but allows the process to be done by other channels, the lack of timers can result in participants waiting forever for a message. It is the participants business to deal with this and other similars. +- The RFC does not describe a Service but just message structure and serialization. ## Testing plan From 0ea8af9f34ed107f1c5627623eeace7b693092dd Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 10 May 2021 09:53:25 -0300 Subject: [PATCH 26/70] fix typos --- rfcs/0001-messages.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index e702874d..697f5ecb 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -87,7 +87,6 @@ Each payload defines a new message: // With this, the participant should be able to build a `SharePackage` and use // the `sign()` function. // `public_key` can be calculated from the `secret_key`. -// from `secret_key`. struct MsgDealerBroadcast { // The secret key as a frost::Scalar. secret_key: frost::Scalar, @@ -119,43 +118,43 @@ struct Commitment { binding: jubjub::AffinePoint, } -// The aggergator decide what message is going to be signed and +// The aggregator decide what message is going to be signed and // send it to each participant with all the commitments collected. struct MsgSigningPackage { // The number of participants. participants: u8, // The collected commitments for each signer - commitments: Vec, - // The lenght of the message + commitments: Vec, + // The length of the message message_length: u64, // The message to be signed as bytes message: &'static [u8], } -// The aggergator collected commitments for each signer in the +// The aggregator collected commitments for each signer in the // scheme. struct CollectedCommitment { - // Signer commitment - signer_id: u8, + // The signer's participant identifier + signer_id: Participant, // Commitment for this signer commitment: Commitment, } -// Each signer send the signatures to the agregator who is going to collect them -// and generate a final spend signature. +// Each signer sends their signatures to the aggregator who is going to collect them +// and generate a final spend signature. struct MsgSignatureShare { // The signature to be shared as a Scalar signature: frost::Scalar, } -// The final signature is broadcasted by the aggegator -// to any participant. +// The final signature is broadcasted by the aggregator +// to any participant. struct MsgFinalSignature { // Bytes needed to build the frost::Signature final_signature: FinalSignature, } -// Final RedJubJub signature the aggergator has created. +// Final RedJubJub signature the aggregator has created. struct FinalSignature { // r_bytes: [u8; 32], From 42763c176dd9ed8220ba42d4de5d7902127f5096 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 10 May 2021 10:02:20 -0300 Subject: [PATCH 27/70] use to_bytes in primitive types and reduce serialization size --- rfcs/0001-messages.md | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 697f5ecb..457db1ac 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -118,8 +118,8 @@ struct Commitment { binding: jubjub::AffinePoint, } -// The aggregator decide what message is going to be signed and -// send it to each participant with all the commitments collected. +// The aggregator decides what message is going to be signed and +// sends it to each participant with all the commitments collected. struct MsgSigningPackage { // The number of participants. participants: u8, @@ -242,21 +242,19 @@ Bytes | Field name | Data type #### `Scalar` -`Scalar` is a an alias for `jubjub::Fr` and this is a `[u64; 4]` as documented in https://github.com/zkcrypto/jubjub/blob/main/src/fr.rs#L16 +`Scalar` is a an alias for `jubjub::Fr`. We use `Scalar::to_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L252 #### `AffinePoint` -Much of the math in FROST is done using `jubjub::ExtendedPoint`. This is a structure with 5 `jubjub::Fq`s as defined in https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L128-L134 - -Each `Fq` needed to form a `jubjub::ExtendedPoint` are `Scalar`s of `bls12_381` crate. Scalar here is `[u64; 4]` as documented in https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L16 - -For message exchange `jubjub::AffinePoint`s are a better choice as they are shorter in bytes, they are formed of 2 `jubjub::Fq` instead of 5: https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L70-L73 +Much of the math in FROST is done using `jubjub::ExtendedPoint`. But for message exchange `jubjub::AffinePoint`s are a better choice, as their byte representation is smaller. Conversion from one type to the other is trivial: https://docs.rs/jubjub/0.6.0/jubjub/struct.AffinePoint.html#impl-From%3CExtendedPoint%3E https://docs.rs/jubjub/0.6.0/jubjub/struct.ExtendedPoint.html#impl-From%3CAffinePoint%3E +We use `AffinePoint::to_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L443 + ### Payload Payload part of the message is variable in size and depends on message type. @@ -265,22 +263,22 @@ Payload part of the message is variable in size and depends on message type. Bytes | Field name | Data type -------|-------------|----------- -256 | secret_key | Scalar -512 | commitments | AffinePoint -512+32 | group_public| GroupPublic +32 | secret_key | Scalar +32 | commitments | AffinePoint +32+32 | group_public| GroupPublic #### `MsgCommitments` Bytes | Field name | Data type --------|------------|----------- -512+512 | commitment | Commitment +32+32 | commitment | Commitment #### `MsgSigningPackage` Bytes | Field name | Data type -----------------------|----------------|----------- 1 | participants | u8 -(1+1024)*partipants | commitments | Vec +(1+32+32)*partipants | commitments | Vec 8 | message_length | u64 message_length | message | [u8] @@ -288,13 +286,13 @@ message_length | message | [u8] Bytes | Field name | Data type ------|------------|----------- -256 | signature | Scalar +32 | signature | Scalar #### `MsgFinalSignature` Bytes | Field name | Data type ------|------------------|----------- -64 | final_signature | FinalSignature +32+32 | final_signature | FinalSignature ## Not included From 81ceee0fdcdfc5a054654156f7d8628aecf32690 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 10 May 2021 10:05:46 -0300 Subject: [PATCH 28/70] add note about large messages --- rfcs/0001-messages.md | 1 + 1 file changed, 1 insertion(+) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 457db1ac..b569ac3f 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -301,6 +301,7 @@ The following are a few things this RFC is not considering: - After the dealer sends the initial `MsgDealerBroadcast` to all the participants, the aggregator must wait for signers to send the second message `MsgCommitments`. There is no timeout for this but only after the aggregator received all the commitments the process can continue. These restrictions and event waiting are not detailed in this RFC. - This implementation considers not only communications between computer devices in the internet but allows the process to be done by other channels, the lack of timers can result in participants waiting forever for a message. It is the participants business to deal with this and other similars. - The RFC does not describe a Service but just message structure and serialization. +- Messages larger than 4 GB are not supported on 32-bit platforms. ## Testing plan From 987cddd7694983b08ae83af51d9900c11504fb71 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 10 May 2021 10:11:01 -0300 Subject: [PATCH 29/70] change comments to doc comments --- rfcs/0001-messages.md | 69 ++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index b569ac3f..fb630b1d 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -83,82 +83,83 @@ struct Participant(u8); Each payload defines a new message: ```rust -// Dealer must send this message with initial data to each participant involved. -// With this, the participant should be able to build a `SharePackage` and use -// the `sign()` function. -// `public_key` can be calculated from the `secret_key`. +/// Dealer must send this message with initial data to each participant involved. +/// With this, the participant should be able to build a `SharePackage` and use +/// the `sign()` function. +/// `public_key` can be calculated from the `secret_key`. struct MsgDealerBroadcast { - // The secret key as a frost::Scalar. + /// The secret key as a frost::Scalar. secret_key: frost::Scalar, - // Commitment for the signer as a single jubjub::AffinePoint. + /// Commitment for the signer as a single jubjub::AffinePoint. commitment: jubjub::AffinePoint, - // The public signing key that represents the entire group. + /// The public signing key that represents the entire group. group_public: GroupPublic, } -// The point and verification bytes needed to generate the group public key +/// The point and verification bytes needed to generate the group public key struct GroupPublic { - // The point + /// The point point: jubjub::AffinePoint, - // The verification bytes + /// The verification bytes bytes: [u8; 32], } -// Each signer participant send to the aggregator the 2 points -// needed for commitment building. +/// Each signer participant send to the aggregator the 2 points +/// needed for commitment building. struct MsgCommitments { + /// The commitment the signer is sending. commitment: Commitment, } -// A commitment specified by two AffinePoints. +/// A commitment specified by two AffinePoints. struct Commitment { - // The hiding Point. + /// The hiding Point. hiding: jubjub::AffinePoint, - // The binding Point. + /// The binding Point. binding: jubjub::AffinePoint, } -// The aggregator decides what message is going to be signed and -// sends it to each participant with all the commitments collected. +/// The aggregator decides what message is going to be signed and +/// sends it to each participant with all the commitments collected. struct MsgSigningPackage { - // The number of participants. + /// The number of participants. participants: u8, - // The collected commitments for each signer + /// The collected commitments for each signer commitments: Vec, - // The length of the message + /// The length of the message message_length: u64, - // The message to be signed as bytes + /// The message to be signed as bytes message: &'static [u8], } -// The aggregator collected commitments for each signer in the -// scheme. +/// The aggregator collected commitments for each signer in the +/// scheme. struct CollectedCommitment { - // The signer's participant identifier + /// The signer's participant identifier signer_id: Participant, - // Commitment for this signer + /// Commitment for this signer commitment: Commitment, } -// Each signer sends their signatures to the aggregator who is going to collect them -// and generate a final spend signature. +/// Each signer sends their signatures to the aggregator who is going to collect them +/// and generate a final spend signature. struct MsgSignatureShare { - // The signature to be shared as a Scalar + /// The signature to be shared as a Scalar signature: frost::Scalar, } -// The final signature is broadcasted by the aggregator -// to any participant. +/// The final signature is broadcasted by the aggregator +/// to any participant. struct MsgFinalSignature { - // Bytes needed to build the frost::Signature + /// Bytes needed to build the frost::Signature final_signature: FinalSignature, } -// Final RedJubJub signature the aggregator has created. +/// Final RedJubJub signature the aggregator has created. struct FinalSignature { - // + /// r_bytes: [u8; 32], - // + /// s_bytes: [u8; 32], } ``` From b82e41465d46ce14c199e433e7ffbb2cb8da4dd4 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 10 May 2021 10:38:10 -0300 Subject: [PATCH 30/70] remove lenght of msg to be signed from rust --- rfcs/0001-messages.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index fb630b1d..b5f560d1 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -126,10 +126,8 @@ struct MsgSigningPackage { participants: u8, /// The collected commitments for each signer commitments: Vec, - /// The length of the message - message_length: u64, - /// The message to be signed as bytes - message: &'static [u8], + /// The message to be signed as a vector of bytes + message: Vec, } /// The aggregator collected commitments for each signer in the @@ -281,7 +279,7 @@ Bytes | Field name | Data type 1 | participants | u8 (1+32+32)*partipants | commitments | Vec 8 | message_length | u64 -message_length | message | [u8] +message_length | message | Vec #### `SignatureShare` From 28aefe2eb5fd82d8a073da1ce6f4acd22f628714 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 10 May 2021 10:49:36 -0300 Subject: [PATCH 31/70] replace CollectedCommitment with a HashMap --- rfcs/0001-messages.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index b5f560d1..da4292bc 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -124,21 +124,13 @@ struct Commitment { struct MsgSigningPackage { /// The number of participants. participants: u8, - /// The collected commitments for each signer - commitments: Vec, + /// The collected commitments for each signer as a hashmap of + /// unique participant identifiers + commitments: HashMap, /// The message to be signed as a vector of bytes message: Vec, } -/// The aggregator collected commitments for each signer in the -/// scheme. -struct CollectedCommitment { - /// The signer's participant identifier - signer_id: Participant, - /// Commitment for this signer - commitment: Commitment, -} - /// Each signer sends their signatures to the aggregator who is going to collect them /// and generate a final spend signature. struct MsgSignatureShare { @@ -277,7 +269,7 @@ Bytes | Field name | Data type Bytes | Field name | Data type -----------------------|----------------|----------- 1 | participants | u8 -(1+32+32)*partipants | commitments | Vec +(1+32+32)*partipants | commitments | HashMap 8 | message_length | u64 message_length | message | Vec From c90f1e3c2f8ff1e0008bf167d543f90b6b4b72ac Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 10 May 2021 10:52:27 -0300 Subject: [PATCH 32/70] remove participants field from rust in MsgSigningPackage --- rfcs/0001-messages.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index da4292bc..096e28e3 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -122,8 +122,6 @@ struct Commitment { /// The aggregator decides what message is going to be signed and /// sends it to each participant with all the commitments collected. struct MsgSigningPackage { - /// The number of participants. - participants: u8, /// The collected commitments for each signer as a hashmap of /// unique participant identifiers commitments: HashMap, From 2293e1a52f589a813d06f9c32a59d81e1d313fef Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 10 May 2021 11:18:44 -0300 Subject: [PATCH 33/70] fix typo --- rfcs/0001-messages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 096e28e3..c32dc795 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -267,7 +267,7 @@ Bytes | Field name | Data type Bytes | Field name | Data type -----------------------|----------------|----------- 1 | participants | u8 -(1+32+32)*partipants | commitments | HashMap +(1+32+32)*participants | commitments | HashMap 8 | message_length | u64 message_length | message | Vec From e14631b355f9ba96bf07a7620502c872e51bf495 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 10 May 2021 11:24:03 -0300 Subject: [PATCH 34/70] change Participant to ParticipantID --- rfcs/0001-messages.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index c32dc795..b04122e0 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -35,8 +35,8 @@ struct Message { struct Header { msg_type: MsgType, version: MsgVersion, - sender: Participant, - receiver: Participant, + sender: ParticipantID, + receiver: ParticipantID, } ``` @@ -75,7 +75,7 @@ enum MsgType { struct MsgVersion(u8); -struct Participant(u8); +struct ParticipantID(u8); ``` ### Payloads @@ -124,7 +124,7 @@ struct Commitment { struct MsgSigningPackage { /// The collected commitments for each signer as a hashmap of /// unique participant identifiers - commitments: HashMap, + commitments: HashMap, /// The message to be signed as a vector of bytes message: Vec, } @@ -267,7 +267,7 @@ Bytes | Field name | Data type Bytes | Field name | Data type -----------------------|----------------|----------- 1 | participants | u8 -(1+32+32)*participants | commitments | HashMap +(1+32+32)*participants | commitments | HashMap 8 | message_length | u64 message_length | message | Vec From 4284ddb85c7beb68315a9d36a6981d6a233477df Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 11 May 2021 09:06:58 -0300 Subject: [PATCH 35/70] update payloads rust code --- rfcs/0001-messages.md | 70 ++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index b04122e0..7b1178af 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -85,70 +85,64 @@ Each payload defines a new message: ```rust /// Dealer must send this message with initial data to each participant involved. /// With this, the participant should be able to build a `SharePackage` and use -/// the `sign()` function. -/// `public_key` can be calculated from the `secret_key`. +/// the `sign()` function. +/// +/// Note: `frost::SharePackage.public` can be calculated from `secret_share`. struct MsgDealerBroadcast { - /// The secret key as a frost::Scalar. - secret_key: frost::Scalar, + /// This participant's secret key share: `frost::Share.value`. + secret_share: frost::Scalar, /// Commitment for the signer as a single jubjub::AffinePoint. - commitment: jubjub::AffinePoint, - /// The public signing key that represents the entire group. - group_public: GroupPublic, -} - -/// The point and verification bytes needed to generate the group public key -struct GroupPublic { - /// The point - point: jubjub::AffinePoint, - /// The verification bytes - bytes: [u8; 32], + /// A set of commitments to the coefficients (which themselves are scalars) + /// for a secret polynomial _f_: `frost::SharePackage.share.commitment` + share_commitment: Vec, + /// The public signing key that represents the entire group: + /// `frost::SharePackage.group_public`. + group_public: jubjub::AffinePoint, } /// Each signer participant send to the aggregator the 2 points /// needed for commitment building. struct MsgCommitments { - /// The commitment the signer is sending. - commitment: Commitment, + /// A commitment to a single signature by this signer: + /// `frost::SigningPackage.signing_commitments` + signing_commitments: SigningCommitments, } -/// A commitment specified by two AffinePoints. -struct Commitment { - /// The hiding Point. +/// A signing commitment from the first round of the signing protocol. +struct SigningCommitments { + /// The hiding point: `frost::SigningCommitments.hiding` hiding: jubjub::AffinePoint, - /// The binding Point. + /// The binding point: `frost::SigningCommitments.binding` binding: jubjub::AffinePoint, } /// The aggregator decides what message is going to be signed and /// sends it to each participant with all the commitments collected. struct MsgSigningPackage { - /// The collected commitments for each signer as a hashmap of - /// unique participant identifiers - commitments: HashMap, - /// The message to be signed as a vector of bytes + /// The message to be signed: `frost::SigningPackage.message` message: Vec, + /// The collected commitments for each signer as a hashmap of + /// unique participant identifiers: `frost::SigningPackage.signing_commitments` + /// + /// Signing packages that contain duplicate or missing `ParticipantID`s are invalid. + signing_commitments: HashMap, } /// Each signer sends their signatures to the aggregator who is going to collect them /// and generate a final spend signature. struct MsgSignatureShare { - /// The signature to be shared as a Scalar + /// This participant's signature over the message: + /// `frost::SignatureShare.signature` signature: frost::Scalar, } -/// The final signature is broadcasted by the aggregator -/// to any participant. +/// The final signature is broadcasted by the aggregator to any participant. struct MsgFinalSignature { - /// Bytes needed to build the frost::Signature - final_signature: FinalSignature, -} - -/// Final RedJubJub signature the aggregator has created. -struct FinalSignature { - /// - r_bytes: [u8; 32], - /// - s_bytes: [u8; 32], + /// The aggregated group commitment: `Signature.r_bytes` returned by `frost::aggregate` + group_commitment: jubjub::AffinePoint, + /// A plain Schnorr signature created by summing all the signature shares: + /// `Signature.s_bytes` returned by `frost::aggregate` + schnorr_signature: frost::Scalar, } ``` From 300e503a8888d823440ce5f110f6b5c423734c6e Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 11 May 2021 09:14:09 -0300 Subject: [PATCH 36/70] add a new not included item --- rfcs/0001-messages.md | 1 + 1 file changed, 1 insertion(+) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 7b1178af..da6cd020 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -285,6 +285,7 @@ The following are a few things this RFC is not considering: - This implementation considers not only communications between computer devices in the internet but allows the process to be done by other channels, the lack of timers can result in participants waiting forever for a message. It is the participants business to deal with this and other similars. - The RFC does not describe a Service but just message structure and serialization. - Messages larger than 4 GB are not supported on 32-bit platforms. +- Implementations should validate that message lengths are lower than protocol-specific maximum length, then allocate message memory. ## Testing plan From b8abdf5dad6b1fbb91bca4dc5dd2e3e6b02eeabb Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 11 May 2021 09:16:58 -0300 Subject: [PATCH 37/70] add line to AffinePoint description --- rfcs/0001-messages.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index da6cd020..a52ae96d 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -238,6 +238,8 @@ https://docs.rs/jubjub/0.6.0/jubjub/struct.ExtendedPoint.html#impl-From%3CAffine We use `AffinePoint::to_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L443 +Similarly, `VerificationKey`s can be serialized using `into::<[u8;32]>`. See https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L86 + ### Payload Payload part of the message is variable in size and depends on message type. From cf84c6f199cf33c70d244ce0c400e9a8cb148c38 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 11 May 2021 09:26:05 -0300 Subject: [PATCH 38/70] update serialization --- rfcs/0001-messages.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index a52ae96d..d5a6d7ff 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -246,11 +246,12 @@ Payload part of the message is variable in size and depends on message type. #### `MsgDealerBroadcast` -Bytes | Field name | Data type --------|-------------|----------- -32 | secret_key | Scalar -32 | commitments | AffinePoint -32+32 | group_public| GroupPublic +Bytes | Field name | Data type +----------------|------------------|----------- +32 | secret_share | Scalar +1 | participants | u8 +32*participants | share_commitment | Vec +32 | group_public | AffinePoint #### `MsgCommitments` @@ -260,12 +261,12 @@ Bytes | Field name | Data type #### `MsgSigningPackage` -Bytes | Field name | Data type ------------------------|----------------|----------- -1 | participants | u8 -(1+32+32)*participants | commitments | HashMap -8 | message_length | u64 -message_length | message | Vec +Bytes | Field name | Data type +-----------------------|--------------------|----------- +1 | participants | u8 +(1+32+32)*participants | signing_commitments| HashMap +8 | message_length | u64 +message_length | message | Vec #### `SignatureShare` @@ -277,7 +278,8 @@ Bytes | Field name | Data type Bytes | Field name | Data type ------|------------------|----------- -32+32 | final_signature | FinalSignature +32 | group_commitment | group_commitment +32 | schnorr_signature| Scalar ## Not included From 0d51a74f7e4c27e6a69314cc6055deba4047f34e Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 11 May 2021 09:42:31 -0300 Subject: [PATCH 39/70] add a new validation rules section --- rfcs/0001-messages.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index d5a6d7ff..6c58acd5 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -197,6 +197,15 @@ if let Ok(header) = msg.header.validate() { } ``` +### Rules + +- Each jubjub type must be validated during deserialization. +- `share_commitments`: For each round, the maximum number of participants is set by the length of `share_commitments`. +- `signing_commitments`: + - Signing packages that contain duplicate or missing `ParticipantID`s are invalid + - The length of `signing_commitments` must be less than or equal to the length of the `share_commitments` for this round. +- `message`: signed messages have a protocol-specific length limit. For Zcash, that limit is the maximum network protocol message length: `2^21` bytes (2 MB). + ## Serialization/Deserialization Each message struct needs to serialize to bytes representation before it is sent through the wire and must deserialize to the same struct (round trip) on the receiver side. We use `serde` and macro derivations (`Serialize` and `Deserialize`) to automatically implement where possible. From 632ed9607f4c5bbfd224fcfede9d90bc42f43dfd Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 11 May 2021 09:50:16 -0300 Subject: [PATCH 40/70] split rules in header and payload sub sections --- rfcs/0001-messages.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 6c58acd5..c16660f4 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -199,6 +199,14 @@ if let Ok(header) = msg.header.validate() { ### Rules +The following rules must be implemented: + +#### Header + +- `sender` and `receiver` can't be the same. + +#### Payloads + - Each jubjub type must be validated during deserialization. - `share_commitments`: For each round, the maximum number of participants is set by the length of `share_commitments`. - `signing_commitments`: From 43a302b397ef3ccb8e06ed9d0641507ddfebae21 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 11 May 2021 09:59:18 -0300 Subject: [PATCH 41/70] fix missing field name and type in MsgCommitments serialization --- rfcs/0001-messages.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index c16660f4..77fa8fe0 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -272,9 +272,9 @@ Bytes | Field name | Data type #### `MsgCommitments` -Bytes | Field name | Data type ---------|------------|----------- -32+32 | commitment | Commitment +Bytes | Field name | Data type +--------|---------------------|----------- +32+32 | signing_commitments | SigningCommitments #### `MsgSigningPackage` From 2d6290e2c56ff54e9f871cf5dd138615fe0c1018 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 11 May 2021 10:05:48 -0300 Subject: [PATCH 42/70] use some escape characters --- rfcs/0001-messages.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 77fa8fe0..3986b0ff 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -267,7 +267,7 @@ Bytes | Field name | Data type ----------------|------------------|----------- 32 | secret_share | Scalar 1 | participants | u8 -32*participants | share_commitment | Vec +32*participants | share_commitment | Vec\ 32 | group_public | AffinePoint #### `MsgCommitments` @@ -283,7 +283,7 @@ Bytes | Field name | Data type 1 | participants | u8 (1+32+32)*participants | signing_commitments| HashMap 8 | message_length | u64 -message_length | message | Vec +message_length | message | Vec\ #### `SignatureShare` From 899ea9e2d725dd65f82471605dfbb8d7e38226e6 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 11 May 2021 10:27:16 -0300 Subject: [PATCH 43/70] add definitions --- rfcs/0001-messages.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 3986b0ff..92b1dd3e 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -11,12 +11,9 @@ Assuming all participants have a FROST library available we need to define messa ## Definitions -- `dealer` -- `aggregator` -- `signer` -- `nonce` -- `commitment` -- +- `dealer` - Participant who distributes the initial package to all the other participants. The dealer can also be the aggregator and one of the signers. +- `aggregator` - Participant in charge of collecting all the signatures from the other participants and generating the final group signature. The aggregator can also be the dealer and one of the signers. +- `signer` - Participant that will receive the initial package, sign and send the signature to the aggregator to receive the final group signature. A signer can be also the dealer and the aggregator. ## Guide-level explanation From 70eb9aa2bdff4e432472d97340484151451fba12 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 13 May 2021 09:20:46 +1000 Subject: [PATCH 44/70] Fix a typo --- rfcs/0001-messages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 92b1dd3e..fd006509 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -303,7 +303,7 @@ The following are a few things this RFC is not considering: - This implementation considers not only communications between computer devices in the internet but allows the process to be done by other channels, the lack of timers can result in participants waiting forever for a message. It is the participants business to deal with this and other similars. - The RFC does not describe a Service but just message structure and serialization. - Messages larger than 4 GB are not supported on 32-bit platforms. -- Implementations should validate that message lengths are lower than protocol-specific maximum length, then allocate message memory. +- Implementations should validate that message lengths are lower than a protocol-specific maximum length, then allocate message memory. ## Testing plan From a3d01a51fb8b360dbb97bb2acbb1425e623584ed Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 13 May 2021 10:36:58 +1000 Subject: [PATCH 45/70] Name, Doc and Serialization Tweaks, and a Test Plan --- rfcs/0001-messages.md | 92 +++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 25 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index fd006509..3b7409a0 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -20,6 +20,7 @@ Assuming all participants have a FROST library available we need to define messa We propose a message separated in 2 parts, a header and a payload: ```rust +/// The data required to serialize a frost message. struct Message { header: Header, payload: Payload, @@ -29,8 +30,10 @@ struct Message { `Header` will look as follows: ```rust +/// The data required to serialize the common header fields for every message. +/// +/// Note: the `msg_type` is derived from the `payload` enum variant. struct Header { - msg_type: MsgType, version: MsgVersion, sender: ParticipantID, receiver: ParticipantID, @@ -40,6 +43,7 @@ struct Header { While `Payload` will be defined as: ```rust +/// The data required to serialize the payload for a message. enum Payload { DealerBroadcast(MsgDealerBroadcast), Commitments(MsgCommitments), @@ -60,6 +64,7 @@ Here we explore in detail the header types and all the message payloads. Fields of the header define new types. Proposed implementation for them is as follows: ```rust +/// The numeric values used to identify each `Payload` variant during serialization. #[repr(u8)] #[non_exhaustive] enum MsgType { @@ -70,9 +75,11 @@ enum MsgType { FinalSignature, } +/// The numeric values used to identify the protocol version during serialization. struct MsgVersion(u8); -struct ParticipantID(u8); +/// The numeric values used to identify each participant during serialization. +struct ParticipantId(u8); ``` ### Payloads @@ -80,13 +87,15 @@ struct ParticipantID(u8); Each payload defines a new message: ```rust +/// The data required to serialize `frost::SharePackage`. +/// /// Dealer must send this message with initial data to each participant involved. /// With this, the participant should be able to build a `SharePackage` and use /// the `sign()` function. /// /// Note: `frost::SharePackage.public` can be calculated from `secret_share`. -struct MsgDealerBroadcast { - /// This participant's secret key share: `frost::Share.value`. +struct messages::SharePackage { + /// This participant's secret key share: `frost::SharePackage.share.value`. secret_share: frost::Scalar, /// Commitment for the signer as a single jubjub::AffinePoint. /// A set of commitments to the coefficients (which themselves are scalars) @@ -97,25 +106,21 @@ struct MsgDealerBroadcast { group_public: jubjub::AffinePoint, } -/// Each signer participant send to the aggregator the 2 points -/// needed for commitment building. -struct MsgCommitments { - /// A commitment to a single signature by this signer: - /// `frost::SigningPackage.signing_commitments` - signing_commitments: SigningCommitments, -} - +/// The data required to serialize `frost::SigningCommitments`. +/// /// A signing commitment from the first round of the signing protocol. -struct SigningCommitments { +struct messages::SigningCommitments { /// The hiding point: `frost::SigningCommitments.hiding` hiding: jubjub::AffinePoint, /// The binding point: `frost::SigningCommitments.binding` binding: jubjub::AffinePoint, } +/// The data required to serialize `frost::SigningPackage`. +/// /// The aggregator decides what message is going to be signed and /// sends it to each participant with all the commitments collected. -struct MsgSigningPackage { +struct messages::SigningPackage { /// The message to be signed: `frost::SigningPackage.message` message: Vec, /// The collected commitments for each signer as a hashmap of @@ -125,16 +130,20 @@ struct MsgSigningPackage { signing_commitments: HashMap, } +/// The data required to serialize `frost::SignatureShare`. +/// /// Each signer sends their signatures to the aggregator who is going to collect them /// and generate a final spend signature. -struct MsgSignatureShare { +struct messages::SignatureShare { /// This participant's signature over the message: /// `frost::SignatureShare.signature` signature: frost::Scalar, } +/// The data required to serialize a successful output from `frost::aggregate()`. +/// /// The final signature is broadcasted by the aggregator to any participant. -struct MsgFinalSignature { +struct messages::AggregateSignature { /// The aggregated group commitment: `Signature.r_bytes` returned by `frost::aggregate` group_commitment: jubjub::AffinePoint, /// A plain Schnorr signature created by summing all the signature shares: @@ -200,14 +209,19 @@ The following rules must be implemented: #### Header +- `msg_type` must be a known `MsgType` value. +- `version` must be a supported version. - `sender` and `receiver` can't be the same. +- `sender` and `receiver` must be less than the maximum number of participants. #### Payloads - Each jubjub type must be validated during deserialization. - `share_commitments`: For each round, the maximum number of participants is set by the length of `share_commitments`. - `signing_commitments`: - - Signing packages that contain duplicate or missing `ParticipantID`s are invalid + - Signing packages that contain duplicate `ParticipantID`s are invalid + - Signing packages that contain missing `ParticipantID`s are invalid + - TODO: check if missing participants are allowed - The length of `signing_commitments` must be less than or equal to the length of the `share_commitments` for this round. - `message`: signed messages have a protocol-specific length limit. For Zcash, that limit is the maximum network protocol message length: `2^21` bytes (2 MB). @@ -239,7 +253,7 @@ Bytes | Field name | Data type #### `Scalar` -`Scalar` is a an alias for `jubjub::Fr`. We use `Scalar::to_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L252 +`Scalar` is a an alias for `jubjub::Fr`. We use `Scalar::to_bytes` and `Scalar::from_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L252 #### `AffinePoint` @@ -250,9 +264,9 @@ Conversion from one type to the other is trivial: https://docs.rs/jubjub/0.6.0/jubjub/struct.AffinePoint.html#impl-From%3CExtendedPoint%3E https://docs.rs/jubjub/0.6.0/jubjub/struct.ExtendedPoint.html#impl-From%3CAffinePoint%3E -We use `AffinePoint::to_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L443 +We use `AffinePoint::to_bytes` and `AffinePoint::from_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L443 -Similarly, `VerificationKey`s can be serialized using `into::<[u8;32]>`. See https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L86 +Similarly, `VerificationKey`s can be serialized using `<[u8; 32]>::from` and `VerificationKey::from`. See https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L86 ### Payload @@ -307,8 +321,36 @@ The following are a few things this RFC is not considering: ## Testing plan -- Create a happy path unit test similar to https://github.com/ZcashFoundation/redjubjub/blob/frost-messages/tests/frost.rs#L7 and: - - Make messages on each step. - - Simulate send/receive. - - Test round trip serialization/deserialization on each message. -- Create property tests for each message. +### Test Vectors + +#### Conversion on Test Vectors + +- Test conversion from `frost` to `message` on a test vector + 1. Implement the Rust `message` struct + 2. Implement conversion from and to the `frost` type + 3. Do a round-trip test from `frost` to `message` on a test vector +- Test conversion from `message` to bytes on a test vector + 1. Implement conversion from and to the `message` type + 2. Do a round-trip test from `message` to bytes on a test vector + +#### Signing Rounds on Test Vectors + +- Test signing using `frost` types on a test vector + 1. Implement a single round of `frost` signing using a test vector +- Test signing using `message` types on a test vector +- Test signing using byte vectors on a test vector + +### Property Tests + +#### Conversion Property Tests + +- Create property tests for each message + - Test round-trip conversion from `frost` to `message` types + - Test round-trip serialization and deserialization for each `message` type + +#### Signing Round Property Tests + +- Create property tests for signing rounds + - Test a signing round with `frost` types + - Test a signing round with `message` types + - Test a signing round with byte vectors From a53b40cf8f7f5c36a0576a7576cf653822d47b78 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 13 May 2021 10:43:54 +1000 Subject: [PATCH 46/70] Remove the separate MsgCommitments type --- rfcs/0001-messages.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 3b7409a0..0ee26de2 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -285,7 +285,8 @@ Bytes | Field name | Data type Bytes | Field name | Data type --------|---------------------|----------- -32+32 | signing_commitments | SigningCommitments +32 | hiding | AffinePoint +32 | binding | AffinePoint #### `MsgSigningPackage` From 8955be9a1f8cfcb800af4fe957c74a47823bc6c3 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 13 May 2021 10:45:26 +1000 Subject: [PATCH 47/70] Fix a missed type --- rfcs/0001-messages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 0ee26de2..26e09f24 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -307,7 +307,7 @@ Bytes | Field name | Data type Bytes | Field name | Data type ------|------------------|----------- -32 | group_commitment | group_commitment +32 | group_commitment | AffinePoint 32 | schnorr_signature| Scalar ## Not included From 284d4650c8b3ea157964b5cd780e84a22c1d7e2d Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 13 May 2021 18:30:11 +1000 Subject: [PATCH 48/70] Add a constant for this serialization version --- rfcs/0001-messages.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 26e09f24..af0b05fb 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -78,6 +78,8 @@ enum MsgType { /// The numeric values used to identify the protocol version during serialization. struct MsgVersion(u8); +const BASIC_FROST_SERIALIZATION: MsgVersion = MsgVersion(0); + /// The numeric values used to identify each participant during serialization. struct ParticipantId(u8); ``` From 86f57730b94b93377fb47c250478ef1f7aa197eb Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 13 May 2021 18:34:36 +1000 Subject: [PATCH 49/70] Reserve specific participant IDs for the dealer and aggregator Also explicitly identify the sender and receiver for each message. --- rfcs/0001-messages.md | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index af0b05fb..6db793b1 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -81,7 +81,25 @@ struct MsgVersion(u8); const BASIC_FROST_SERIALIZATION: MsgVersion = MsgVersion(0); /// The numeric values used to identify each participant during serialization. -struct ParticipantId(u8); +/// +/// In the `frost` module, participant ID `0` should be invalid. +/// But in serialization, we want participants to be indexed from `0..n`, +/// where `n` is the number of participants. +/// This helps us look up their shares and commitments in serialized arrays. +/// So in serialization, we assign the dealer and aggregator to IDs `254` and `255`. +/// +/// "When performing Shamir secret sharing, a polynomial `f(x)` is used to generate each party’s share of the secret. The actual secret is `f(0)` and the party with ID `i` will be given a share with value `f(i)`. Since a DKG may be implemented in the future, we recommend that the ID `0` be declared invalid." +/// https://raw.githubusercontent.com/ZcashFoundation/redjubjub/main/zcash-frost-audit-report-20210323.pdf#d +enum ParticipantId { + Signer(u8), + Dealer, + Aggregator, +} + +/// The maximum `ParticipantId::Signer` in this serialization format. +/// +/// We reserve two participant IDs for the dealer and aggregator. +const MAX_SIGNER_PARTICIPANT_ID: u8 = u8::MAX - 2; ``` ### Payloads @@ -91,8 +109,8 @@ Each payload defines a new message: ```rust /// The data required to serialize `frost::SharePackage`. /// -/// Dealer must send this message with initial data to each participant involved. -/// With this, the participant should be able to build a `SharePackage` and use +/// The dealer sends this message to each signer for this round. +/// With this, the signer should be able to build a `SharePackage` and use /// the `sign()` function. /// /// Note: `frost::SharePackage.public` can be calculated from `secret_share`. @@ -110,6 +128,7 @@ struct messages::SharePackage { /// The data required to serialize `frost::SigningCommitments`. /// +/// Each signer must send this message to the aggregator. /// A signing commitment from the first round of the signing protocol. struct messages::SigningCommitments { /// The hiding point: `frost::SigningCommitments.hiding` @@ -121,7 +140,7 @@ struct messages::SigningCommitments { /// The data required to serialize `frost::SigningPackage`. /// /// The aggregator decides what message is going to be signed and -/// sends it to each participant with all the commitments collected. +/// sends it to each signer with all the commitments collected. struct messages::SigningPackage { /// The message to be signed: `frost::SigningPackage.message` message: Vec, @@ -144,7 +163,7 @@ struct messages::SignatureShare { /// The data required to serialize a successful output from `frost::aggregate()`. /// -/// The final signature is broadcasted by the aggregator to any participant. +/// The final signature is broadcasted by the aggregator to all signers. struct messages::AggregateSignature { /// The aggregated group commitment: `Signature.r_bytes` returned by `frost::aggregate` group_commitment: jubjub::AffinePoint, @@ -214,17 +233,19 @@ The following rules must be implemented: - `msg_type` must be a known `MsgType` value. - `version` must be a supported version. - `sender` and `receiver` can't be the same. -- `sender` and `receiver` must be less than the maximum number of participants. +- If `sender` and `receiver` are a `ParticipantId::Signer`, they must be less than the number of participants in this round. +- The `ParticipantId` variants of `sender` and `receiver` must match the message type. #### Payloads - Each jubjub type must be validated during deserialization. -- `share_commitments`: For each round, the maximum number of participants is set by the length of `share_commitments`. +- `share_commitments`: The number of participants in each round is set by the length of `share_commitments`. + - The number of participants in each round must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. - `signing_commitments`: - Signing packages that contain duplicate `ParticipantID`s are invalid - Signing packages that contain missing `ParticipantID`s are invalid - TODO: check if missing participants are allowed - - The length of `signing_commitments` must be less than or equal to the length of the `share_commitments` for this round. + - The length of `signing_commitments` must be less than or equal to the number of participants in this round. - `message`: signed messages have a protocol-specific length limit. For Zcash, that limit is the maximum network protocol message length: `2^21` bytes (2 MB). ## Serialization/Deserialization From 79eda4ef6c885d4462fe36ea5d13baff383249bc Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 13 May 2021 18:39:17 +1000 Subject: [PATCH 50/70] Clarify participant ID changes --- rfcs/0001-messages.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 6db793b1..fb439ea5 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -86,16 +86,28 @@ const BASIC_FROST_SERIALIZATION: MsgVersion = MsgVersion(0); /// But in serialization, we want participants to be indexed from `0..n`, /// where `n` is the number of participants. /// This helps us look up their shares and commitments in serialized arrays. -/// So in serialization, we assign the dealer and aggregator to IDs `254` and `255`. +/// So in serialization, we assign the dealer and aggregator the highest IDs, +/// and mark those IDs as invalid for signers. /// /// "When performing Shamir secret sharing, a polynomial `f(x)` is used to generate each party’s share of the secret. The actual secret is `f(0)` and the party with ID `i` will be given a share with value `f(i)`. Since a DKG may be implemented in the future, we recommend that the ID `0` be declared invalid." /// https://raw.githubusercontent.com/ZcashFoundation/redjubjub/main/zcash-frost-audit-report-20210323.pdf#d enum ParticipantId { + /// A serialized participant ID for a signer. + /// + /// Must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. Signer(u8), + /// The fixed participant ID for the dealer. Dealer, + /// The fixed participant ID for the aggregator. Aggregator, } +/// The fixed participant ID for the dealer. +const DEALER_PARTICIPANT_ID: u8 = u8::MAX - 1; + +/// The fixed participant ID for the aggregator. +const AGGREGATOR_PARTICIPANT_ID: u8 = u8::MAX; + /// The maximum `ParticipantId::Signer` in this serialization format. /// /// We reserve two participant IDs for the dealer and aggregator. From 9db4453deda83f1eb8694b81c87aad49842a7dfa Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 13 May 2021 09:13:53 -0300 Subject: [PATCH 51/70] fix missing msg names --- rfcs/0001-messages.md | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index fb439ea5..b53163b4 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -45,11 +45,11 @@ While `Payload` will be defined as: ```rust /// The data required to serialize the payload for a message. enum Payload { - DealerBroadcast(MsgDealerBroadcast), - Commitments(MsgCommitments), - SigningPackage(MsgSigningPackage), - SignatureShare(MsgSignatureShare), - FinalSignature(MsgFinalSignature), + SharePackage(messages::SharePackage), + SigningCommitments(messages::SigningCommitments), + SigningPackage(messages::SigningPackage), + SignatureShare(messages::SignatureShare), + AggregateSignature(messages::AggregateSignature), } ``` @@ -68,11 +68,11 @@ Fields of the header define new types. Proposed implementation for them is as fo #[repr(u8)] #[non_exhaustive] enum MsgType { - DealerBroadcast, - Commitments, + SharePackage, + SigningCommitments, SigningPackage, SignatureShare, - FinalSignature, + AggregateSignature, } /// The numeric values used to identify the protocol version during serialization. @@ -307,7 +307,7 @@ Similarly, `VerificationKey`s can be serialized using `<[u8; 32]>::from` and `Ve Payload part of the message is variable in size and depends on message type. -#### `MsgDealerBroadcast` +#### `SharePackage` Bytes | Field name | Data type ----------------|------------------|----------- @@ -316,21 +316,22 @@ Bytes | Field name | Data type 32*participants | share_commitment | Vec\ 32 | group_public | AffinePoint -#### `MsgCommitments` +#### `SigningCommitments` Bytes | Field name | Data type --------|---------------------|----------- 32 | hiding | AffinePoint 32 | binding | AffinePoint -#### `MsgSigningPackage` +#### `SigningPackage` Bytes | Field name | Data type -----------------------|--------------------|----------- -1 | participants | u8 -(1+32+32)*participants | signing_commitments| HashMap 8 | message_length | u64 message_length | message | Vec\ +1 | participants | u8 +(1+32+32)*participants | signing_commitments| HashMap + #### `SignatureShare` @@ -338,7 +339,7 @@ Bytes | Field name | Data type ------|------------|----------- 32 | signature | Scalar -#### `MsgFinalSignature` +#### `AggregateSignature` Bytes | Field name | Data type ------|------------------|----------- @@ -349,7 +350,7 @@ Bytes | Field name | Data type The following are a few things this RFC is not considering: -- After the dealer sends the initial `MsgDealerBroadcast` to all the participants, the aggregator must wait for signers to send the second message `MsgCommitments`. There is no timeout for this but only after the aggregator received all the commitments the process can continue. These restrictions and event waiting are not detailed in this RFC. +- After the dealer sends the initial `SharePackage` to all the participants, the aggregator must wait for signers to send the second message `SigningCommitments`. There is no timeout for this but only after the aggregator received all the commitments the process can continue. These restrictions and event waiting are not detailed in this RFC. - This implementation considers not only communications between computer devices in the internet but allows the process to be done by other channels, the lack of timers can result in participants waiting forever for a message. It is the participants business to deal with this and other similars. - The RFC does not describe a Service but just message structure and serialization. - Messages larger than 4 GB are not supported on 32-bit platforms. From ed337b680301a7231e4e681f90818300dde94cbf Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 13 May 2021 09:22:42 -0300 Subject: [PATCH 52/70] split doc comment in lines --- rfcs/0001-messages.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index b53163b4..a87f14b4 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -89,7 +89,11 @@ const BASIC_FROST_SERIALIZATION: MsgVersion = MsgVersion(0); /// So in serialization, we assign the dealer and aggregator the highest IDs, /// and mark those IDs as invalid for signers. /// -/// "When performing Shamir secret sharing, a polynomial `f(x)` is used to generate each party’s share of the secret. The actual secret is `f(0)` and the party with ID `i` will be given a share with value `f(i)`. Since a DKG may be implemented in the future, we recommend that the ID `0` be declared invalid." +/// "When performing Shamir secret sharing, a polynomial `f(x)` is used to generate +/// each party’s share of the secret. The actual secret is `f(0)` and the party with +/// ID `i` will be given a share with value `f(i)`. +/// Since a DKG may be implemented in the future, we recommend that the ID `0` +/// be declared invalid." /// https://raw.githubusercontent.com/ZcashFoundation/redjubjub/main/zcash-frost-audit-report-20210323.pdf#d enum ParticipantId { /// A serialized participant ID for a signer. From 5f3184e4f9c45d697e5df9c64ac751984b60dbaf Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 13 May 2021 09:28:25 -0300 Subject: [PATCH 53/70] remove some spaces after newlines --- rfcs/0001-messages.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index a87f14b4..29862440 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -92,8 +92,7 @@ const BASIC_FROST_SERIALIZATION: MsgVersion = MsgVersion(0); /// "When performing Shamir secret sharing, a polynomial `f(x)` is used to generate /// each party’s share of the secret. The actual secret is `f(0)` and the party with /// ID `i` will be given a share with value `f(i)`. -/// Since a DKG may be implemented in the future, we recommend that the ID `0` -/// be declared invalid." +/// Since a DKG may be implemented in the future, we recommend that the ID `0` be declared invalid." /// https://raw.githubusercontent.com/ZcashFoundation/redjubjub/main/zcash-frost-audit-report-20210323.pdf#d enum ParticipantId { /// A serialized participant ID for a signer. @@ -135,10 +134,10 @@ struct messages::SharePackage { secret_share: frost::Scalar, /// Commitment for the signer as a single jubjub::AffinePoint. /// A set of commitments to the coefficients (which themselves are scalars) - /// for a secret polynomial _f_: `frost::SharePackage.share.commitment` + /// for a secret polynomial _f_: `frost::SharePackage.share.commitment` share_commitment: Vec, /// The public signing key that represents the entire group: - /// `frost::SharePackage.group_public`. + /// `frost::SharePackage.group_public`. group_public: jubjub::AffinePoint, } @@ -170,10 +169,9 @@ struct messages::SigningPackage { /// The data required to serialize `frost::SignatureShare`. /// /// Each signer sends their signatures to the aggregator who is going to collect them -/// and generate a final spend signature. +/// and generate a final spend signature. struct messages::SignatureShare { - /// This participant's signature over the message: - /// `frost::SignatureShare.signature` + /// This participant's signature over the message: `frost::SignatureShare.signature` signature: frost::Scalar, } From e892d30af341b809e102dbc8f6aa53aa3e629d4c Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 13 May 2021 09:55:45 -0300 Subject: [PATCH 54/70] fix definitions after changes to ParticipantID --- rfcs/0001-messages.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 29862440..a72451e4 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -11,9 +11,11 @@ Assuming all participants have a FROST library available we need to define messa ## Definitions -- `dealer` - Participant who distributes the initial package to all the other participants. The dealer can also be the aggregator and one of the signers. -- `aggregator` - Participant in charge of collecting all the signatures from the other participants and generating the final group signature. The aggregator can also be the dealer and one of the signers. -- `signer` - Participant that will receive the initial package, sign and send the signature to the aggregator to receive the final group signature. A signer can be also the dealer and the aggregator. +- `dealer` - Participant who distributes the initial package to all the other participants. +- `aggregator` - Participant in charge of collecting all the signatures from the other participants and generating the final group signature. +- `signer` - Participant that will receive the initial package, sign and send the signature to the aggregator to receive the final group signature. + +Note: In this RFC we consider the above 3 participants to be different. `dealer` and `aggergator` have specific hard coded `ParticipantId`s so for example a `dealer` can't be a `signer`. This is not a protocol limitation but a specific rule introduced in this document. ## Guide-level explanation From 111eab26f08ab534b7f79c1eeea8e4a96f3d9d5c Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 13 May 2021 10:59:43 -0300 Subject: [PATCH 55/70] replace ParticipantID with ParticipantId everywhere remaining --- rfcs/0001-messages.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index a72451e4..d9df4385 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -37,8 +37,8 @@ struct Message { /// Note: the `msg_type` is derived from the `payload` enum variant. struct Header { version: MsgVersion, - sender: ParticipantID, - receiver: ParticipantID, + sender: ParticipantId, + receiver: ParticipantId, } ``` @@ -164,8 +164,8 @@ struct messages::SigningPackage { /// The collected commitments for each signer as a hashmap of /// unique participant identifiers: `frost::SigningPackage.signing_commitments` /// - /// Signing packages that contain duplicate or missing `ParticipantID`s are invalid. - signing_commitments: HashMap, + /// Signing packages that contain duplicate or missing `ParticipantId`s are invalid. + signing_commitments: HashMap, } /// The data required to serialize `frost::SignatureShare`. @@ -258,8 +258,8 @@ The following rules must be implemented: - `share_commitments`: The number of participants in each round is set by the length of `share_commitments`. - The number of participants in each round must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. - `signing_commitments`: - - Signing packages that contain duplicate `ParticipantID`s are invalid - - Signing packages that contain missing `ParticipantID`s are invalid + - Signing packages that contain duplicate `ParticipantId`s are invalid + - Signing packages that contain missing `ParticipantId`s are invalid - TODO: check if missing participants are allowed - The length of `signing_commitments` must be less than or equal to the number of participants in this round. - `message`: signed messages have a protocol-specific length limit. For Zcash, that limit is the maximum network protocol message length: `2^21` bytes (2 MB). @@ -334,7 +334,7 @@ Bytes | Field name | Data type 8 | message_length | u64 message_length | message | Vec\ 1 | participants | u8 -(1+32+32)*participants | signing_commitments| HashMap +(1+32+32)*participants | signing_commitments| HashMap #### `SignatureShare` From a1b92665cf3303f5bda3b2affd04f00e1fa74f3e Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 14 May 2021 09:26:16 +1000 Subject: [PATCH 56/70] Make state-based validation out of scope --- rfcs/0001-messages.md | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index d9df4385..5dc1b210 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -249,19 +249,18 @@ The following rules must be implemented: - `msg_type` must be a known `MsgType` value. - `version` must be a supported version. - `sender` and `receiver` can't be the same. -- If `sender` and `receiver` are a `ParticipantId::Signer`, they must be less than the number of participants in this round. - The `ParticipantId` variants of `sender` and `receiver` must match the message type. #### Payloads - Each jubjub type must be validated during deserialization. -- `share_commitments`: The number of participants in each round is set by the length of `share_commitments`. - - The number of participants in each round must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. +- `share_commitments`: length must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. - `signing_commitments`: + - Length must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. - Signing packages that contain duplicate `ParticipantId`s are invalid - Signing packages that contain missing `ParticipantId`s are invalid - - TODO: check if missing participants are allowed - - The length of `signing_commitments` must be less than or equal to the number of participants in this round. + - Note: missing participants are supported by this serialization format. + But implementations can require all participants to fully participate in each round. - `message`: signed messages have a protocol-specific length limit. For Zcash, that limit is the maximum network protocol message length: `2^21` bytes (2 MB). ## Serialization/Deserialization @@ -354,12 +353,22 @@ Bytes | Field name | Data type The following are a few things this RFC is not considering: -- After the dealer sends the initial `SharePackage` to all the participants, the aggregator must wait for signers to send the second message `SigningCommitments`. There is no timeout for this but only after the aggregator received all the commitments the process can continue. These restrictions and event waiting are not detailed in this RFC. -- This implementation considers not only communications between computer devices in the internet but allows the process to be done by other channels, the lack of timers can result in participants waiting forever for a message. It is the participants business to deal with this and other similars. -- The RFC does not describe a Service but just message structure and serialization. +- The RFC does not describe implementation-specific issues - it is focused on message structure and serialization. +- Implementations using this serialization should handle missing messages using timeouts or similar protocol-specific mechanisms. + - This is particularly important for `SigningPackage`s, which only need a threshold of participants to continue. - Messages larger than 4 GB are not supported on 32-bit platforms. - Implementations should validate that message lengths are lower than a protocol-specific maximum length, then allocate message memory. +### State-Based Validation + +The following validation rules should be checked by the implementation: + +- `share_commitments`: The number of participants in each round is set by the length of `share_commitments`. + - If `sender` and `receiver` are a `ParticipantId::Signer`, they must be less than the number of participants in this round. + - The length of `signing_commitments` must be less than or equal to the number of participants in this round. + +If the implementation knows the number of key shares, it should re-check all the validation rules involving `MAX_SIGNER_PARTICIPANT_ID` using that lower limit. + ## Testing plan ### Test Vectors From c92b892a0217f5d1e7613e06029b82744a32dbd1 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 14 May 2021 09:32:42 -0300 Subject: [PATCH 57/70] remove `msg_type` validation rule --- rfcs/0001-messages.md | 1 - 1 file changed, 1 deletion(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 5dc1b210..62b92d28 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -246,7 +246,6 @@ The following rules must be implemented: #### Header -- `msg_type` must be a known `MsgType` value. - `version` must be a supported version. - `sender` and `receiver` can't be the same. - The `ParticipantId` variants of `sender` and `receiver` must match the message type. From 21c309fba90ea67cd3720a34e0369756694aa7d9 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 14 May 2021 16:06:40 -0300 Subject: [PATCH 58/70] change some validation rules --- rfcs/0001-messages.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 62b92d28..7add2b27 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -253,13 +253,13 @@ The following rules must be implemented: #### Payloads - Each jubjub type must be validated during deserialization. -- `share_commitments`: length must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. +- `share_commitments`: + - Length must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. + - Lenght must be at least `MIN_SIGNERS` (`2` signers). - `signing_commitments`: - Length must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. - - Signing packages that contain duplicate `ParticipantId`s are invalid - - Signing packages that contain missing `ParticipantId`s are invalid - - Note: missing participants are supported by this serialization format. - But implementations can require all participants to fully participate in each round. + - Signing packages that contain duplicate `ParticipantId`s are invalid. This is implicit in the use of `HashMap`. + - Length must be at least `MIN_THRESHOLD` (`2` required signers). - `message`: signed messages have a protocol-specific length limit. For Zcash, that limit is the maximum network protocol message length: `2^21` bytes (2 MB). ## Serialization/Deserialization @@ -365,6 +365,9 @@ The following validation rules should be checked by the implementation: - `share_commitments`: The number of participants in each round is set by the length of `share_commitments`. - If `sender` and `receiver` are a `ParticipantId::Signer`, they must be less than the number of participants in this round. - The length of `signing_commitments` must be less than or equal to the number of participants in this round. +- `signing_commitments`: Signing packages that contain missing `ParticipantId`s are invalid + - Note: missing participants are supported by this serialization format. + But implementations can require all participants to fully participate in each round. If the implementation knows the number of key shares, it should re-check all the validation rules involving `MAX_SIGNER_PARTICIPANT_ID` using that lower limit. From 29904a45dfaee20dfa2e22c7165588a11eb865b0 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 17 May 2021 10:31:17 -0300 Subject: [PATCH 59/70] forbid multi-bytes in serialization Co-authored-by: teor --- rfcs/0001-messages.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 7add2b27..f96752d9 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -305,6 +305,8 @@ We use `AffinePoint::to_bytes` and `AffinePoint::from_bytes` to get a 32-byte li Similarly, `VerificationKey`s can be serialized using `<[u8; 32]>::from` and `VerificationKey::from`. See https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L86 +Multi-byte integers **must not** be used for serialization, because they have different byte orders on different platforms. + ### Payload Payload part of the message is variable in size and depends on message type. From 1f5f04f9c8107d0754124bf9c0c0fd31c1c5d605 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 17 May 2021 16:22:03 -0300 Subject: [PATCH 60/70] change group_public --- rfcs/0001-messages.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index f96752d9..f347cb7d 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -140,7 +140,7 @@ struct messages::SharePackage { share_commitment: Vec, /// The public signing key that represents the entire group: /// `frost::SharePackage.group_public`. - group_public: jubjub::AffinePoint, + group_public: VerificationKey, } /// The data required to serialize `frost::SigningCommitments`. @@ -273,6 +273,8 @@ Manual implementation of serialization/deserialization will be located at a new Each byte chunk specified below is in little-endian order unless is specified otherwise. +Multi-byte integers **must not** be used for serialization, because they have different byte orders on different platforms. + ### Header The `Header` part of the message is 4 bytes total: @@ -286,7 +288,7 @@ Bytes | Field name | Data type ### Primitive types -`Payload`s use data types that we need to specify first. We have 2 primitive types inside the payload messages: +`Payload`s use data types that we need to specify first. We have 3 primitive types inside the payload messages: #### `Scalar` @@ -303,9 +305,9 @@ https://docs.rs/jubjub/0.6.0/jubjub/struct.ExtendedPoint.html#impl-From%3CAffine We use `AffinePoint::to_bytes` and `AffinePoint::from_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L443 -Similarly, `VerificationKey`s can be serialized using `<[u8; 32]>::from` and `VerificationKey::from`. See https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L86 +#### VerificationKey -Multi-byte integers **must not** be used for serialization, because they have different byte orders on different platforms. +`VerificationKey`s can be serialized and deserialized using `<[u8; 32]>::from` and `VerificationKey::from`. See https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L80-L90 and https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L114-L121. ### Payload @@ -318,7 +320,7 @@ Bytes | Field name | Data type 32 | secret_share | Scalar 1 | participants | u8 32*participants | share_commitment | Vec\ -32 | group_public | AffinePoint +32 | group_public | VerificationKey #### `SigningCommitments` From 3c8494d753aa19c076e6bf65802e1e6f4ccb2cb6 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Wed, 19 May 2021 08:46:01 -0300 Subject: [PATCH 61/70] put variable-length fields last in messages Co-authored-by: teor --- rfcs/0001-messages.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index f347cb7d..ce82743b 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -132,15 +132,15 @@ Each payload defines a new message: /// /// Note: `frost::SharePackage.public` can be calculated from `secret_share`. struct messages::SharePackage { + /// The public signing key that represents the entire group: + /// `frost::SharePackage.group_public`. + group_public: VerificationKey, /// This participant's secret key share: `frost::SharePackage.share.value`. secret_share: frost::Scalar, /// Commitment for the signer as a single jubjub::AffinePoint. /// A set of commitments to the coefficients (which themselves are scalars) /// for a secret polynomial _f_: `frost::SharePackage.share.commitment` share_commitment: Vec, - /// The public signing key that represents the entire group: - /// `frost::SharePackage.group_public`. - group_public: VerificationKey, } /// The data required to serialize `frost::SigningCommitments`. @@ -159,13 +159,15 @@ struct messages::SigningCommitments { /// The aggregator decides what message is going to be signed and /// sends it to each signer with all the commitments collected. struct messages::SigningPackage { - /// The message to be signed: `frost::SigningPackage.message` - message: Vec, /// The collected commitments for each signer as a hashmap of /// unique participant identifiers: `frost::SigningPackage.signing_commitments` /// /// Signing packages that contain duplicate or missing `ParticipantId`s are invalid. signing_commitments: HashMap, + /// The message to be signed: `frost::SigningPackage.message`. + /// + /// Each signer should perform protocol-specific verification on the message. + message: Vec, } /// The data required to serialize `frost::SignatureShare`. @@ -317,10 +319,10 @@ Payload part of the message is variable in size and depends on message type. Bytes | Field name | Data type ----------------|------------------|----------- +32 | group_public | VerificationKey 32 | secret_share | Scalar 1 | participants | u8 32*participants | share_commitment | Vec\ -32 | group_public | VerificationKey #### `SigningCommitments` @@ -333,10 +335,10 @@ Bytes | Field name | Data type Bytes | Field name | Data type -----------------------|--------------------|----------- -8 | message_length | u64 -message_length | message | Vec\ 1 | participants | u8 (1+32+32)*participants | signing_commitments| HashMap +8 | message_length | u64 +message_length | message | Vec\ #### `SignatureShare` From 74df415b1f95c0d297ebe36c67812f9c6bf9d8f7 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 20 May 2021 18:05:12 -0300 Subject: [PATCH 62/70] text updates Co-authored-by: Deirdre Connolly --- rfcs/0001-messages.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index ce82743b..c0061572 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -5,9 +5,9 @@ Proposes a message layout to exchange information between participants of a FROS ## Motivation Currently FROST library is complete for 2 round signatures with a dealer/aggregator setup. -This proposal is only considering that specific features, additions and upgrades will need to be made when DKG is implemented. +This proposal acknowledges that specific features, additions and upgrades will need to be made when DKG is implemented. -Assuming all participants have a FROST library available we need to define message structures in a way that data can be exchanged between participants. The proposal is a collection of data types so each side can do all the actions needed for a real life situation. +Assuming all participants have a FROST library available, we need to define message structures in a way that data can be exchanged between participants. The proposal is a collection of data types so each side can do all the actions needed for a real life situation. ## Definitions @@ -15,7 +15,7 @@ Assuming all participants have a FROST library available we need to define messa - `aggregator` - Participant in charge of collecting all the signatures from the other participants and generating the final group signature. - `signer` - Participant that will receive the initial package, sign and send the signature to the aggregator to receive the final group signature. -Note: In this RFC we consider the above 3 participants to be different. `dealer` and `aggergator` have specific hard coded `ParticipantId`s so for example a `dealer` can't be a `signer`. This is not a protocol limitation but a specific rule introduced in this document. +Note: In this RFC we consider the above 3 participants to be different. `dealer` and `aggregator` have specific hard coded `ParticipantId`s, so for example a `dealer` can't be a `signer`. This is not a protocol limitation but a specific rule introduced in this document. ## Guide-level explanation From 9b057f28789f108ae9eb8a984ac25b2dedc8088b Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 21 May 2021 19:20:53 -0300 Subject: [PATCH 63/70] change ParticipantId to u64 --- rfcs/0001-messages.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index c0061572..89d1d342 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -100,7 +100,7 @@ enum ParticipantId { /// A serialized participant ID for a signer. /// /// Must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. - Signer(u8), + Signer(u64), /// The fixed participant ID for the dealer. Dealer, /// The fixed participant ID for the aggregator. @@ -108,15 +108,15 @@ enum ParticipantId { } /// The fixed participant ID for the dealer. -const DEALER_PARTICIPANT_ID: u8 = u8::MAX - 1; +const DEALER_PARTICIPANT_ID: u64 = u64::MAX - 1; /// The fixed participant ID for the aggregator. -const AGGREGATOR_PARTICIPANT_ID: u8 = u8::MAX; +const AGGREGATOR_PARTICIPANT_ID: u64 = u64::MAX; /// The maximum `ParticipantId::Signer` in this serialization format. /// /// We reserve two participant IDs for the dealer and aggregator. -const MAX_SIGNER_PARTICIPANT_ID: u8 = u8::MAX - 2; +const MAX_SIGNER_PARTICIPANT_ID: u64 = u64::MAX - 2; ``` ### Payloads @@ -285,8 +285,8 @@ Bytes | Field name | Data type ------|------------|----------- 1 | msg_type | u8 1 | version | u8 -1 | sender | u8 -1 | receiver | u8 +1 | sender | u64 +1 | receiver | u64 ### Primitive types From 83794c56bb6d899f765bbbef6f20a70f6a5d55cc Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 21 May 2021 20:00:01 -0300 Subject: [PATCH 64/70] change some primitives to frost types --- rfcs/0001-messages.md | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 89d1d342..50073a27 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -136,11 +136,11 @@ struct messages::SharePackage { /// `frost::SharePackage.group_public`. group_public: VerificationKey, /// This participant's secret key share: `frost::SharePackage.share.value`. - secret_share: frost::Scalar, + secret_share: frost::Secret, /// Commitment for the signer as a single jubjub::AffinePoint. /// A set of commitments to the coefficients (which themselves are scalars) /// for a secret polynomial _f_: `frost::SharePackage.share.commitment` - share_commitment: Vec, + share_commitment: Vec, } /// The data required to serialize `frost::SigningCommitments`. @@ -149,9 +149,9 @@ struct messages::SharePackage { /// A signing commitment from the first round of the signing protocol. struct messages::SigningCommitments { /// The hiding point: `frost::SigningCommitments.hiding` - hiding: jubjub::AffinePoint, + hiding: frost::Commitment, /// The binding point: `frost::SigningCommitments.binding` - binding: jubjub::AffinePoint, + binding: frost::Commitment, } /// The data required to serialize `frost::SigningPackage`. @@ -184,7 +184,7 @@ struct messages::SignatureShare { /// The final signature is broadcasted by the aggregator to all signers. struct messages::AggregateSignature { /// The aggregated group commitment: `Signature.r_bytes` returned by `frost::aggregate` - group_commitment: jubjub::AffinePoint, + group_commitment: frost::GroupCommitment, /// A plain Schnorr signature created by summing all the signature shares: /// `Signature.s_bytes` returned by `frost::aggregate` schnorr_signature: frost::Scalar, @@ -288,13 +288,21 @@ Bytes | Field name | Data type 1 | sender | u64 1 | receiver | u64 +### Frost types + +The FROST types we will be using in the messages can be represented always as a primitive type. For serialization/deserialization purposes: + +- `Commitment` = `AffinePoint` +- `Secret` = `Scalar` +- `GroupCommitment` = `AffinePoint` + ### Primitive types `Payload`s use data types that we need to specify first. We have 3 primitive types inside the payload messages: #### `Scalar` -`Scalar` is a an alias for `jubjub::Fr`. We use `Scalar::to_bytes` and `Scalar::from_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L252 +`jubjub::Scalar` is a an alias for `jubjub::Fr`. We use `Scalar::to_bytes` and `Scalar::from_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L252 #### `AffinePoint` @@ -309,7 +317,7 @@ We use `AffinePoint::to_bytes` and `AffinePoint::from_bytes` to get a 32-byte li #### VerificationKey -`VerificationKey`s can be serialized and deserialized using `<[u8; 32]>::from` and `VerificationKey::from`. See https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L80-L90 and https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L114-L121. +`redjubjub::VerificationKey`s can be serialized and deserialized using `<[u8; 32]>::from` and `VerificationKey::from`. See https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L80-L90 and https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L114-L121. ### Payload @@ -320,16 +328,16 @@ Payload part of the message is variable in size and depends on message type. Bytes | Field name | Data type ----------------|------------------|----------- 32 | group_public | VerificationKey -32 | secret_share | Scalar +32 | secret_share | Share 1 | participants | u8 -32*participants | share_commitment | Vec\ +32*participants | share_commitment | Vec\ #### `SigningCommitments` Bytes | Field name | Data type --------|---------------------|----------- -32 | hiding | AffinePoint -32 | binding | AffinePoint +32 | hiding | Commitment +32 | binding | Commitment #### `SigningPackage` @@ -351,7 +359,7 @@ Bytes | Field name | Data type Bytes | Field name | Data type ------|------------------|----------- -32 | group_commitment | AffinePoint +32 | group_commitment | GroupCommitment 32 | schnorr_signature| Scalar ## Not included From f9e73afb23c9fc33fa1285d7825271472c879683 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 21 May 2021 20:04:30 -0300 Subject: [PATCH 65/70] update a field description --- rfcs/0001-messages.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 50073a27..69ee51ab 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -137,9 +137,9 @@ struct messages::SharePackage { group_public: VerificationKey, /// This participant's secret key share: `frost::SharePackage.share.value`. secret_share: frost::Secret, - /// Commitment for the signer as a single jubjub::AffinePoint. - /// A set of commitments to the coefficients (which themselves are scalars) - /// for a secret polynomial _f_: `frost::SharePackage.share.commitment` + /// The commitments to the coefficients for our secret polynomial _f_, + /// used to generate participants' key shares. Participants use these to perform + /// verifiable secret sharing. share_commitment: Vec, } From 189efc20845d02723996abedbcdb3f4857e65dcb Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Sat, 22 May 2021 08:27:09 -0300 Subject: [PATCH 66/70] use FrostSignature instead of Scalar primitive --- rfcs/0001-messages.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 69ee51ab..7d9895c2 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -176,7 +176,7 @@ struct messages::SigningPackage { /// and generate a final spend signature. struct messages::SignatureShare { /// This participant's signature over the message: `frost::SignatureShare.signature` - signature: frost::Scalar, + signature: frost::FrostSignature, } /// The data required to serialize a successful output from `frost::aggregate()`. @@ -187,7 +187,7 @@ struct messages::AggregateSignature { group_commitment: frost::GroupCommitment, /// A plain Schnorr signature created by summing all the signature shares: /// `Signature.s_bytes` returned by `frost::aggregate` - schnorr_signature: frost::Scalar, + schnorr_signature: frost::FrostSignature, } ``` @@ -295,6 +295,7 @@ The FROST types we will be using in the messages can be represented always as a - `Commitment` = `AffinePoint` - `Secret` = `Scalar` - `GroupCommitment` = `AffinePoint` +- `FrostSignature` = `Scalar` ### Primitive types @@ -302,7 +303,7 @@ The FROST types we will be using in the messages can be represented always as a #### `Scalar` -`jubjub::Scalar` is a an alias for `jubjub::Fr`. We use `Scalar::to_bytes` and `Scalar::from_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L252 +`jubjub::Scalar` is a an alias for `jubjub::Fr`. We use `Scalar::to_bytes` and `Scalar::from_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L260 and https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L232 #### `AffinePoint` @@ -353,14 +354,14 @@ message_length | message | Vec\ Bytes | Field name | Data type ------|------------|----------- -32 | signature | Scalar +32 | signature | FrostSignature #### `AggregateSignature` Bytes | Field name | Data type ------|------------------|----------- 32 | group_commitment | GroupCommitment -32 | schnorr_signature| Scalar +32 | schnorr_signature| FrostSignature ## Not included From fa886e3ff6d9360a314a3355576fa9446288a37d Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Wed, 26 May 2021 12:40:34 -0300 Subject: [PATCH 67/70] change type to `SignatureResponse` --- rfcs/0001-messages.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 7d9895c2..b3c7248c 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -176,7 +176,7 @@ struct messages::SigningPackage { /// and generate a final spend signature. struct messages::SignatureShare { /// This participant's signature over the message: `frost::SignatureShare.signature` - signature: frost::FrostSignature, + signature: frost::SignatureResponse, } /// The data required to serialize a successful output from `frost::aggregate()`. @@ -187,7 +187,7 @@ struct messages::AggregateSignature { group_commitment: frost::GroupCommitment, /// A plain Schnorr signature created by summing all the signature shares: /// `Signature.s_bytes` returned by `frost::aggregate` - schnorr_signature: frost::FrostSignature, + schnorr_signature: frost::SignatureResponse, } ``` @@ -295,7 +295,7 @@ The FROST types we will be using in the messages can be represented always as a - `Commitment` = `AffinePoint` - `Secret` = `Scalar` - `GroupCommitment` = `AffinePoint` -- `FrostSignature` = `Scalar` +- `SignatureResponse` = `Scalar` ### Primitive types @@ -354,14 +354,14 @@ message_length | message | Vec\ Bytes | Field name | Data type ------|------------|----------- -32 | signature | FrostSignature +32 | signature | SignatureResponse #### `AggregateSignature` Bytes | Field name | Data type ------|------------------|----------- 32 | group_commitment | GroupCommitment -32 | schnorr_signature| FrostSignature +32 | schnorr_signature| SignatureResponse ## Not included From c4cb05501e633a7d7f4cbdadf9ebfb3ac617baaf Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 31 May 2021 08:59:02 -0300 Subject: [PATCH 68/70] use BTreeMap, update sizes, etc Co-authored-by: teor --- rfcs/0001-messages.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index b3c7248c..36ac2a83 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -89,7 +89,8 @@ const BASIC_FROST_SERIALIZATION: MsgVersion = MsgVersion(0); /// where `n` is the number of participants. /// This helps us look up their shares and commitments in serialized arrays. /// So in serialization, we assign the dealer and aggregator the highest IDs, -/// and mark those IDs as invalid for signers. +/// and mark those IDs as invalid for signers. Then we serialize the +/// participants in numeric order of their FROST IDs. /// /// "When performing Shamir secret sharing, a polynomial `f(x)` is used to generate /// each party’s share of the secret. The actual secret is `f(0)` and the party with @@ -159,11 +160,12 @@ struct messages::SigningCommitments { /// The aggregator decides what message is going to be signed and /// sends it to each signer with all the commitments collected. struct messages::SigningPackage { - /// The collected commitments for each signer as a hashmap of + /// The collected commitments for each signer as an ordered map of /// unique participant identifiers: `frost::SigningPackage.signing_commitments` /// /// Signing packages that contain duplicate or missing `ParticipantId`s are invalid. - signing_commitments: HashMap, + /// `ParticipantId`s must be serialized in ascending numeric order. + signing_commitments: BTreeMap, /// The message to be signed: `frost::SigningPackage.message`. /// /// Each signer should perform protocol-specific verification on the message. @@ -260,7 +262,8 @@ The following rules must be implemented: - Lenght must be at least `MIN_SIGNERS` (`2` signers). - `signing_commitments`: - Length must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. - - Signing packages that contain duplicate `ParticipantId`s are invalid. This is implicit in the use of `HashMap`. + - Signing packages that contain duplicate `ParticipantId`s are invalid. This is implicit in the use of `BTreeMap`. + - Signing packages must serialize in ascending numeric `ParticipantId` order. This is the order of `BTreeMap.iter`. - Length must be at least `MIN_THRESHOLD` (`2` required signers). - `message`: signed messages have a protocol-specific length limit. For Zcash, that limit is the maximum network protocol message length: `2^21` bytes (2 MB). @@ -279,14 +282,14 @@ Multi-byte integers **must not** be used for serialization, because they have di ### Header -The `Header` part of the message is 4 bytes total: +The `Header` part of the message is 18 bytes total: Bytes | Field name | Data type ------|------------|----------- -1 | msg_type | u8 1 | version | u8 -1 | sender | u64 -1 | receiver | u64 +1 | msg_type | u8 +8 | sender | u64 +8 | receiver | u64 ### Frost types @@ -345,7 +348,7 @@ Bytes | Field name | Data type Bytes | Field name | Data type -----------------------|--------------------|----------- 1 | participants | u8 -(1+32+32)*participants | signing_commitments| HashMap +(8+32+32)*participants | signing_commitments| BTreeMap 8 | message_length | u64 message_length | message | Vec\ From de1f62568d924aa99dc9d1a024b66965835d0239 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 31 May 2021 09:10:10 -0300 Subject: [PATCH 69/70] Convert `share_commitment` to a BTreeMap Co-authored-by: teor --- rfcs/0001-messages.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 36ac2a83..5c9c441e 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -141,7 +141,9 @@ struct messages::SharePackage { /// The commitments to the coefficients for our secret polynomial _f_, /// used to generate participants' key shares. Participants use these to perform /// verifiable secret sharing. - share_commitment: Vec, + /// Share packages that contain duplicate or missing `ParticipantId`s are invalid. + /// `ParticipantId`s must be serialized in ascending numeric order. + share_commitment: BTreeMap, } /// The data required to serialize `frost::SigningCommitments`. @@ -334,7 +336,7 @@ Bytes | Field name | Data type 32 | group_public | VerificationKey 32 | secret_share | Share 1 | participants | u8 -32*participants | share_commitment | Vec\ +(8+32)*participants | share_commitment | BTreeMap #### `SigningCommitments` From fa74969d5653c620e831c8c51c096f051527d958 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 1 Jun 2021 09:30:53 -0300 Subject: [PATCH 70/70] add missing checks Co-authored-by: teor --- rfcs/0001-messages.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rfcs/0001-messages.md b/rfcs/0001-messages.md index 5c9c441e..d130451b 100644 --- a/rfcs/0001-messages.md +++ b/rfcs/0001-messages.md @@ -261,12 +261,14 @@ The following rules must be implemented: - Each jubjub type must be validated during deserialization. - `share_commitments`: - Length must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. - - Lenght must be at least `MIN_SIGNERS` (`2` signers). + - Length must be at least `MIN_SIGNERS` (`2` signers). + - Duplicate `ParticipantId`s are invalid. This is implicit in the use of `BTreeMap` during serialization, but must be checked during deserialization. + - Commitments must be serialized in ascending numeric `ParticipantId` order. This is the order of `BTreeMap.iter` during serialization, but must be checked during deserialization. - `signing_commitments`: - Length must be less than or equal to `MAX_SIGNER_PARTICIPANT_ID`. - - Signing packages that contain duplicate `ParticipantId`s are invalid. This is implicit in the use of `BTreeMap`. - - Signing packages must serialize in ascending numeric `ParticipantId` order. This is the order of `BTreeMap.iter`. - Length must be at least `MIN_THRESHOLD` (`2` required signers). + - Signing packages that contain duplicate `ParticipantId`s are invalid. This is implicit in the use of `BTreeMap` during serialization, but must be checked during deserialization. + - Signing packages must serialize in ascending numeric `ParticipantId` order. This is the order of `BTreeMap.iter` during serialization, but must be checked during deserialization.. - `message`: signed messages have a protocol-specific length limit. For Zcash, that limit is the maximum network protocol message length: `2^21` bytes (2 MB). ## Serialization/Deserialization @@ -377,6 +379,7 @@ The following are a few things this RFC is not considering: - This is particularly important for `SigningPackage`s, which only need a threshold of participants to continue. - Messages larger than 4 GB are not supported on 32-bit platforms. - Implementations should validate that message lengths are lower than a protocol-specific maximum length, then allocate message memory. +- Implementations should distinguish between FROST messages from different signature schemes using implementation-specific mechanisms. ### State-Based Validation