Skip to content

Commit

Permalink
add default serialization format
Browse files Browse the repository at this point in the history
  • Loading branch information
conradoplg committed Sep 1, 2023
1 parent a057497 commit a143ec2
Show file tree
Hide file tree
Showing 62 changed files with 982 additions and 28 deletions.
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)
- [Terminology](terminology.md)
- [Developer Documentation](dev.md)
- [List of Dependencies for Audit](dev/frost-dependencies-for-audit.md)
24 changes: 18 additions & 6 deletions book/src/tutorial/importing.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,30 @@ 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]
frost-ristretto255 = { version = "0.6.0", features = ["serde"] }
```

Note that serde usage is optional. Applications can use different encodings, and
to suppor that, all structures that need to be transmitted have public getters
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.
11 changes: 9 additions & 2 deletions frost-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ edition = "2021"
# - Update CHANGELOG.md
# - Create git tag.
version = "0.6.0"
authors = ["Deirdre Connolly <[email protected]>", "Chelsea Komlo <[email protected]>", "Conrado Gouvea <[email protected]>"]
authors = [
"Deirdre Connolly <[email protected]>",
"Chelsea Komlo <[email protected]>",
"Conrado Gouvea <[email protected]>",
]
readme = "README.md"
license = "MIT OR Apache-2.0"
repository = "https://github.com/ZcashFoundation/frost"
Expand All @@ -18,9 +22,11 @@ features = ["nightly"]

[dependencies]
byteorder = "1.4"
const-crc32 = "1.2.0"
debugless-unwrap = "0.0.4"
derive-getters = "0.3.0"
hex = { version = "0.4.3", features = ["serde"] }
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 @@ -43,9 +49,10 @@ serde_json = "1.0"

[features]
nightly = []
default = []
default = ["serialization"]
internals = []
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
9 changes: 8 additions & 1 deletion frost-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ pub enum Error<C: Ciphersuite> {
/// The participant's commitment is missing from the Signing Package
#[error("The Signing Package must contain the participant's Commitment.")]
MissingCommitment,

/// The participant's commitment is incorrect
#[error("The participant's commitment is incorrect.")]
IncorrectCommitment,
Expand Down Expand Up @@ -99,6 +98,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 @@ -144,6 +149,8 @@ where
| Error::InvalidCoefficient
| Error::UnknownIdentifier
| Error::IncorrectNumberOfIdentifiers
| 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 @@ -25,8 +25,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 @@ -302,6 +302,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 @@ -603,6 +620,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 @@ -673,6 +706,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 @@ -142,6 +160,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 @@ -183,6 +203,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 @@ -290,6 +290,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

0 comments on commit a143ec2

Please sign in to comment.