Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

merge queue: embarking main (9752182) and #511 together #536

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [Signing](tutorial/signing.md)
- [Distributed Key Generation](tutorial/dkg.md)
- [User Documentation](user.md)
- [Serialization Format](user/serialization.md)
- [FROST with Zcash](zcash.md)
- [Technical Details](zcash/technical-details.md)
- [Ywallet Demo](zcash/ywallet-demo.md)
Expand Down
22 changes: 17 additions & 5 deletions book/src/tutorial/importing.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,21 @@ generation or signing procedure.

FROST is a distributed protocol and thus it requires sending messages between
participants. While the ZF FROST library does not handle communication, it can
help with serialization by activating the `serde` feature. When it is enabled,
you can use [serde](https://serde.rs/) to serialize any structure that needs
to be transmitted. Import example:
help with serialization in the following ways:

### Default byte-oriented serialization

With the `serialization` feature, which is enabled by default, all structs that
need to communicated will have `serialize()` and `deserialize()` methods. The
serialization format is described in [Serialization
Format](../user/serialization.md).

### serde

Alternatively, if you would like to user another format such as JSON, you can
enable the `serde` feature (which is *not* enabled by default). When it is
enabled, you can use [serde](https://serde.rs/) to serialize any structure that
needs to be transmitted. The importing would look like:

```
[dependencies]
Expand All @@ -33,5 +45,5 @@ Note that serde usage is optional. Applications can use different encodings, and
to support that, all structures that need to be transmitted have public getters
and `new()` methods allowing the application to encode and decode them as it
wishes. (Note that fields like `Scalar` and `Element` do have standard byte
string encodings; the application can encode those byte strings as it wishes,
as well the structure themselves and things like maps and lists.)
string encodings; the application can encode those byte strings as it wishes, as
well the structure themselves and things like maps and lists.)
60 changes: 60 additions & 0 deletions book/src/user/serialization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Serialization Format

With the `serialization` feature, which is enabled by default, all structs that
need to communicated will have `serialize()` and `deserialize()` methods.

The format is basically the `serde` encoding of the structs using the
[`postcard`](https://docs.rs/postcard/latest/postcard/) crate.

- Integers are encoded in [varint
format](https://postcard.jamesmunns.com/wire-format#varint-encoded-integers)
- Fixed-size byte arrays are encoded as-is (e.g. scalars, elements)
- Note that the encoding of scalars and elements are defined by the
ciphersuites.
- Variable-size byte arrays are encoded with a length prefix (varint-encoded)
and the array as-is (e.g. the message)
- Maps are encoded as the varint-encoded item count, followed by concatenated
item encodings.
- Ciphersuite IDs are encoded as the 4-byte CRC-32 of the ID string.
- Structs are encoded as the concatenation of the encodings of its items.

For example, the following Signing Package:

- Commitments (map):
- Identifier (byte array): `2a00000000000000000000000000000000000000000000000000000000000000`
- Signing Commitments:
- Hiding (byte array): `e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76`
- Bindng (byte array): `6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919`
- Ciphersuite ID: `"FROST(ristretto255, SHA-512)"`
- Message (variable size byte array): `68656c6c6f20776f726c64` (`"hello world"` in UTF-8)
- Ciphersuite ID (4 bytes): `"FROST(ristretto255, SHA-512)"`

Is encoded as

```
012a000000000000000000000000000000000000000000000000000000000000
00e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d
766a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b9
19e6811b690b68656c6c6f20776f726c64e6811b69
```

- `01`: the length of the map
- `2a00000000000000000000000000000000000000000000000000000000000000`: the identifier
- `e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76`: the hinding commitment
- `6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919`: the binding commitment
- `e6811b69`: the ciphersuite ID of the SigningCommitments, CRC-32 of "FROST(ristretto255, SHA-512)"
- `0b`: the length of the message
- `68656c6c6f20776f726c64`: the message
- `e6811b69`: the ciphersuite ID of the SigningPackage, CRC-32 of "FROST(ristretto255, SHA-512)"

```admonish note
The ciphersuite ID is encoded multiple times in this case because `SigningPackage` includes
`SigningCommitments`, which also need to be communicated in Round 1 and thus also encodes
its ciphersuite ID. This is the only instance where this happens.
```

## Test Vectors

Check the
[`snapshots`](https://github.com/search?q=repo%3AZcashFoundation%2Ffrost+path%3Asnapshots&type=code)
files in each ciphersuite crate for test vectors.
5 changes: 4 additions & 1 deletion frost-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
byteorder = "1.4"
const-crc32 = "1.2.0"
document-features = "0.2.7"
debugless-unwrap = "0.0.4"
derive-getters = "0.3.0"
hex = "0.4.3"
postcard = { version = "1.0.0", features = ["use-std"], optional = true }
rand_core = "0.6"
serde = { version = "1.0.160", features = ["derive"], optional = true }
serdect = { version = "0.2.0", optional = true }
Expand All @@ -48,7 +50,7 @@ rand_chacha = "0.3"
serde_json = "1.0"

[features]
default = []
default = ["serialization"]
#! ## Features
## Expose internal types, which do not have SemVer guarantees. This is an advanced
## feature which can be useful if you need to build a modified version of FROST.
Expand All @@ -58,6 +60,7 @@ internals = []
## can use `serde` to serialize structs with any encoder that supports
## `serde` (e.g. JSON with `serde_json`).
serde = ["dep:serde", "dep:serdect"]
serialization = ["serde", "dep:postcard"]
# Exposes ciphersuite-generic tests for other crates to use
test-impl = ["proptest", "serde_json", "criterion"]

Expand Down
8 changes: 8 additions & 0 deletions frost-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ pub enum Error<C: Ciphersuite> {
/// The ciphersuite does not support deriving identifiers from strings.
#[error("The ciphersuite does not support deriving identifiers from strings.")]
IdentifierDerivationNotSupported,
/// Error serializing value.
#[error("Error serializing value.")]
SerializationError,
/// Error deserializing value.
#[error("Error deserializing value.")]
DeserializationError,
}

impl<C> Error<C>
Expand Down Expand Up @@ -147,6 +153,8 @@ where
| Error::UnknownIdentifier
| Error::IncorrectNumberOfIdentifiers
| Error::IncorrectNumberOfCommitments
| Error::SerializationError
| Error::DeserializationError
| Error::IdentifierDerivationNotSupported => None,
}
}
Expand Down
20 changes: 18 additions & 2 deletions frost-core/src/frost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ pub mod round1;
pub mod round2;

use crate::{
scalar_mul::VartimeMultiscalarMul, Ciphersuite, Element, Error, Field, Group, Scalar,
Signature, VerifyingKey,
scalar_mul::VartimeMultiscalarMul, Ciphersuite, Deserialize, Element, Error, Field, Group,
Scalar, Serialize, Signature, VerifyingKey,
};

pub use self::identifier::Identifier;
Expand Down Expand Up @@ -306,6 +306,22 @@ where
}
}

#[cfg(feature = "serialization")]
impl<C> SigningPackage<C>
where
C: Ciphersuite + serde::Serialize + for<'de> serde::Deserialize<'de>,
{
/// Serialize the struct into a Vec.
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}

/// Deserialize the struct from a slice of bytes.
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}

/// The product of all signers' individual commitments, published as part of the
/// final signature.
#[derive(Clone, PartialEq, Eq)]
Expand Down
51 changes: 50 additions & 1 deletion frost-core/src/frost/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use rand_core::{CryptoRng, RngCore};
use zeroize::{DefaultIsZeroes, Zeroize};

use crate::{
frost::Identifier, Ciphersuite, Element, Error, Field, Group, Scalar, SigningKey, VerifyingKey,
frost::Identifier, Ciphersuite, Deserialize, Element, Error, Field, Group, Scalar, Serialize,
SigningKey, VerifyingKey,
};

#[cfg(feature = "serde")]
Expand Down Expand Up @@ -425,6 +426,22 @@ where
}
}

#[cfg(feature = "serialization")]
impl<C> SecretShare<C>
where
C: Ciphersuite + serde::Serialize + for<'de> serde::Deserialize<'de>,
{
/// Serialize the struct into a Vec.
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}

/// Deserialize the struct from a slice of bytes.
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}

/// The identifier list to use when generating key shares.
pub enum IdentifierList<'a, C: Ciphersuite> {
/// Use the default values (1 to max_signers, inclusive).
Expand Down Expand Up @@ -613,6 +630,22 @@ where
}
}

#[cfg(feature = "serialization")]
impl<C> KeyPackage<C>
where
C: Ciphersuite + serde::Serialize + for<'de> serde::Deserialize<'de>,
{
/// Serialize the struct into a Vec.
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}

/// Deserialize the struct from a slice of bytes.
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}

impl<C> TryFrom<SecretShare<C>> for KeyPackage<C>
where
C: Ciphersuite,
Expand Down Expand Up @@ -684,6 +717,22 @@ where
}
}

#[cfg(feature = "serialization")]
impl<C> PublicKeyPackage<C>
where
C: Ciphersuite + serde::Serialize + for<'de> serde::Deserialize<'de>,
{
/// Serialize the struct into a Vec.
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}

/// Deserialize the struct from a slice of bytes.
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}

fn validate_num_of_signers<C: Ciphersuite>(
min_signers: u16,
max_signers: u16,
Expand Down
36 changes: 36 additions & 0 deletions frost-core/src/frost/keys/dkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ pub mod round1 {
use derive_getters::Getters;
use zeroize::Zeroize;

use crate::{Deserialize, Serialize};

use super::*;

/// The package that must be broadcast by each participant to all other participants
Expand Down Expand Up @@ -92,6 +94,22 @@ pub mod round1 {
}
}

#[cfg(feature = "serialization")]
impl<C> Package<C>
where
C: Ciphersuite + serde::Serialize + for<'de> serde::Deserialize<'de>,
{
/// Serialize the struct into a Vec.
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}

/// Deserialize the struct from a slice of bytes.
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}

/// The secret package that must be kept in memory by the participant
/// between the first and second parts of the DKG protocol (round 1).
///
Expand Down Expand Up @@ -145,6 +163,8 @@ pub mod round2 {
use derive_getters::Getters;
use zeroize::Zeroize;

use crate::{Deserialize, Serialize};

use super::*;

/// A package that must be sent by each participant to some other participants
Expand Down Expand Up @@ -186,6 +206,22 @@ pub mod round2 {
}
}

#[cfg(feature = "serialization")]
impl<C> Package<C>
where
C: Ciphersuite + serde::Serialize + for<'de> serde::Deserialize<'de>,
{
/// Serialize the struct into a Vec.
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}

/// Deserialize the struct from a slice of bytes.
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}

/// The secret package that must be kept in memory by the participant
/// between the second and third parts of the DKG protocol (round 2).
///
Expand Down
18 changes: 17 additions & 1 deletion frost-core/src/frost/round1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use hex::FromHex;
use rand_core::{CryptoRng, RngCore};
use zeroize::Zeroize;

use crate::{frost, Ciphersuite, Element, Error, Field, Group, Scalar};
use crate::{frost, Ciphersuite, Deserialize, Element, Error, Field, Group, Scalar, Serialize};

#[cfg(feature = "serde")]
use crate::ElementSerialization;
Expand Down Expand Up @@ -311,6 +311,22 @@ where
}
}

#[cfg(feature = "serialization")]
impl<C> SigningCommitments<C>
where
C: Ciphersuite + serde::Serialize + for<'de> serde::Deserialize<'de>,
{
/// Serialize the struct into a Vec.
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}

/// Deserialize the struct from a slice of bytes.
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}

impl<C> From<&SigningNonces<C>> for SigningCommitments<C>
where
C: Ciphersuite,
Expand Down
Loading
Loading