Skip to content

Commit

Permalink
add no-std support (ZcashFoundation#621)
Browse files Browse the repository at this point in the history
* add no-std support

* add no-std support to ciphersuite crates

* fix ci

* update changelog, use 'dep:' where appropriate

* additional fixes

* fix clippy issue
  • Loading branch information
conradoplg authored Jun 20, 2024
1 parent 647da35 commit 57c0b7b
Show file tree
Hide file tree
Showing 43 changed files with 246 additions and 140 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ jobs:
- run: cargo install cargo-all-features
- run: cargo build-all-features

build_no_std:
name: build with no_std
runs-on: ubuntu-latest
# Skip ed448 which does not support it.
strategy:
matrix:
crate: [ristretto255, ed25519, p256, secp256k1, rerandomized]
steps:
- uses: actions/[email protected]
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
targets: thumbv6m-none-eabi
- run: cargo build -p frost-${{ matrix.crate }} --no-default-features --target thumbv6m-none-eabi
- run: cargo build -p frost-${{ matrix.crate }} --no-default-features --features serialization --target thumbv6m-none-eabi

test_beta:
name: test on beta
runs-on: ubuntu-latest
Expand Down
10 changes: 8 additions & 2 deletions frost-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,21 @@ Entries are listed in reverse chronological order.
will result in a coherence error in future Rust versions (see #625). In the
unlikely case you're using this, you can replace e.g. `scalar *= identifier`
with `scalar = identifier * scalar`.
* Add no-std support to all crates except frost-ed448. To use, do not enable the
`std` feature that is enabled by default (i.e. use `default-features =
false`); Note that it always links to an external `alloc` crate (i.e. there is
no `alloc` feature). When disabling `std`, the only impact in the API is that
`Error` will no longer implement the `std::error::Error` trait. This is a
breaking change if you are disabling default features but rely on `Error`
implementing `std::error::Error`. In that case, simply enable the `std`
feature.

## 1.0.1

* Fixed `no-default-features`, previously it wouldn't compile.
* Fixed some feature handling that would include unneeded dependencies in some
cases.

## Released

## 1.0.0

* Exposed the `SigningKey::from_scalar()` and `to_scalar()` methods. This
Expand Down
21 changes: 12 additions & 9 deletions frost-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,21 @@ features = ["serde"]
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
byteorder = "1.4"
const-crc32 = "1.2.0"
byteorder = { version = "1.4", default-features = false }
const-crc32 = { version = "1.2.0", package = "const-crc32-nostd" }
document-features = "0.2.7"
debugless-unwrap = "0.0.4"
derive-getters = "0.4.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 }
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
postcard = { version = "1.0.0", features = ["alloc"], optional = true }
rand_core = { version = "0.6", default-features = false }
serde = { version = "1.0.160", default-features = false, features = ["derive"], optional = true }
serdect = { version = "0.2.0", optional = true }
thiserror = "1.0.29"
thiserror-nostd-notrait = { version = "1.0.29", default-features = false }
thiserror = { version = "1.0.29", default-features = false, optional = true }
visibility = "0.1.0"
zeroize = { version = "1.5.4", default-features = false, features = ["derive"] }
itertools = "0.13.0"
itertools = { version = "0.13.0", default-features = false }

# Test dependencies used with the test-impl feature
proptest = { version = "1.0", optional = true }
Expand All @@ -50,8 +51,10 @@ rand_chacha = "0.3"
serde_json = "1.0"

[features]
default = ["serialization", "cheater-detection"]
default = ["serialization", "cheater-detection", "std"]
#! ## Features
## Enable standard library support.
std = ["dep:thiserror"]
## 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.
## The docs won't list them, you will need to check the source code.
Expand Down
8 changes: 4 additions & 4 deletions frost-core/src/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
//! of caller code (which must assemble a batch of signatures across
//! work-items), and loss of the ability to easily pinpoint failing signatures.
use std::iter::once;

use rand_core::{CryptoRng, RngCore};

use crate::{scalar_mul::VartimeMultiscalarMul, Ciphersuite, Element, *};
Expand Down Expand Up @@ -136,7 +134,7 @@ where
VKs.push(item.vk.to_element());
}

let scalars = once(&P_coeff_acc)
let scalars = core::iter::once(&P_coeff_acc)
.chain(VK_coeffs.iter())
.chain(R_coeffs.iter());

Expand All @@ -159,6 +157,8 @@ where
C: Ciphersuite,
{
fn default() -> Self {
Self { signatures: vec![] }
Self {
signatures: Vec::new(),
}
}
}
8 changes: 5 additions & 3 deletions frost-core/src/benches.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//! Ciphersuite-generic benchmark functions.
#![allow(clippy::unwrap_used)]

use std::collections::BTreeMap;
use core::iter;

use criterion::{BenchmarkId, Criterion, Throughput};
use alloc::{collections::BTreeMap, format, vec::Vec};
use rand_core::{CryptoRng, RngCore};

use criterion::{BenchmarkId, Criterion, Throughput};

use crate as frost;
use crate::{batch, Ciphersuite, Signature, SigningKey, VerifyingKey};

Expand All @@ -18,7 +20,7 @@ fn sigs_with_distinct_keys<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
rng: &mut R,
) -> impl Iterator<Item = Item<C>> {
let mut rng = rng.clone();
std::iter::repeat_with(move || {
iter::repeat_with(move || {
let msg = b"Bench";
let sk = SigningKey::new(&mut rng);
let vk = VerifyingKey::from(&sk);
Expand Down
4 changes: 4 additions & 0 deletions frost-core/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
//! FROST Error types
#[cfg(feature = "std")]
use thiserror::Error;

#[cfg(not(feature = "std"))]
use thiserror_nostd_notrait::Error;

use crate::{Ciphersuite, Identifier};

#[derive(Error, Debug, Clone, Copy, Eq, PartialEq)]
Expand Down
10 changes: 5 additions & 5 deletions frost-core/src/identifier.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! FROST participant identifiers
use std::{
use core::{
fmt::{self, Debug},
hash::{Hash, Hasher},
};
Expand Down Expand Up @@ -117,7 +117,7 @@ impl<C> Ord for Identifier<C>
where
C: Ciphersuite,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let serialized_self = <<C::Group as Group>::Field>::little_endian_serialize(&self.0);
let serialized_other = <<C::Group as Group>::Field>::little_endian_serialize(&other.0);
// The default cmp uses lexicographic order; so we need the elements in big endian
Expand All @@ -133,12 +133,12 @@ impl<C> PartialOrd for Identifier<C>
where
C: Ciphersuite,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}

impl<C> std::ops::Mul<Scalar<C>> for Identifier<C>
impl<C> core::ops::Mul<Scalar<C>> for Identifier<C>
where
C: Ciphersuite,
{
Expand All @@ -149,7 +149,7 @@ where
}
}

impl<C> std::ops::Sub for Identifier<C>
impl<C> core::ops::Sub for Identifier<C>
where
C: Ciphersuite,
{
Expand Down
17 changes: 9 additions & 8 deletions frost-core/src/keys.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
//! FROST keys, keygen, key shares
#![allow(clippy::type_complexity)]

use std::{
collections::{BTreeMap, BTreeSet, HashSet},
convert::TryFrom,
default::Default,
use core::iter;

use alloc::{
collections::{BTreeMap, BTreeSet},
fmt::{self, Debug},
iter,
string::ToString,
vec::Vec,
};

use derive_getters::Getters;
Expand Down Expand Up @@ -128,7 +129,7 @@ impl<C> Debug for SigningShare<C>
where
C: Ciphersuite,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("SigningShare").field(&"<redacted>").finish()
}
}
Expand Down Expand Up @@ -310,7 +311,7 @@ impl<C> Debug for CoefficientCommitment<C>
where
C: Ciphersuite,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("CoefficientCommitment")
.field(
&self
Expand Down Expand Up @@ -874,7 +875,7 @@ pub(crate) fn generate_secret_shares<C: Ciphersuite>(
let (coefficients, commitment) =
generate_secret_polynomial(secret, max_signers, min_signers, coefficients)?;

let identifiers_set: HashSet<_> = identifiers.iter().collect();
let identifiers_set: BTreeSet<_> = identifiers.iter().collect();
if identifiers_set.len() != identifiers.len() {
return Err(Error::DuplicatedIdentifier);
}
Expand Down
22 changes: 14 additions & 8 deletions frost-core/src/keys/dkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
//! [Feldman's VSS]: https://www.cs.umd.edu/~gasarch/TOPICS/secretsharing/feldmanVSS.pdf
//! [secure broadcast channel]: https://frost.zfnd.org/terminology.html#broadcast-channel
use std::{collections::BTreeMap, iter};
use core::iter;

use alloc::collections::BTreeMap;

use rand_core::{CryptoRng, RngCore};

Expand All @@ -39,6 +41,9 @@ use crate::{
SigningKey, VerifyingKey,
};

#[cfg(feature = "serialization")]
use crate::serialization::{Deserialize, Serialize};

use super::{
evaluate_polynomial, generate_coefficients, generate_secret_polynomial,
validate_num_of_signers, KeyPackage, PublicKeyPackage, SecretShare, SigningShare,
Expand All @@ -47,14 +52,15 @@ use super::{

/// DKG Round 1 structures.
pub mod round1 {
use alloc::vec::Vec;
use derive_getters::Getters;
use zeroize::Zeroize;

use super::*;

#[cfg(feature = "serialization")]
use crate::serialization::{Deserialize, Serialize};

use super::*;

/// The package that must be broadcast by each participant to all other participants
/// between the first and second parts of the DKG protocol (round 1).
#[derive(Clone, Debug, PartialEq, Eq, Getters)]
Expand Down Expand Up @@ -136,11 +142,11 @@ pub mod round1 {
}
}

impl<C> std::fmt::Debug for SecretPackage<C>
impl<C> core::fmt::Debug for SecretPackage<C>
where
C: Ciphersuite,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SecretPackage")
.field("identifier", &self.identifier)
.field("coefficients", &"<redacted>")
Expand Down Expand Up @@ -169,7 +175,7 @@ pub mod round2 {
use zeroize::Zeroize;

#[cfg(feature = "serialization")]
use crate::serialization::{Deserialize, Serialize};
use alloc::vec::Vec;

use super::*;

Expand Down Expand Up @@ -241,11 +247,11 @@ pub mod round2 {
pub(crate) max_signers: u16,
}

impl<C> std::fmt::Debug for SecretPackage<C>
impl<C> core::fmt::Debug for SecretPackage<C>
where
C: Ciphersuite,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SecretPackage")
.field("identifier", &self.identifier)
.field("commitment", &self.commitment)
Expand Down
4 changes: 3 additions & 1 deletion frost-core/src/keys/repairable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
//! The RTS is used to help a signer (participant) repair their lost share. This is achieved
//! using a subset of the other signers know here as `helpers`.
use std::collections::{BTreeMap, BTreeSet};
use alloc::collections::{BTreeMap, BTreeSet};

use alloc::vec::Vec;

use crate::{
compute_lagrange_coefficient, Ciphersuite, CryptoRng, Error, Field, Group, Header, Identifier,
Expand Down
19 changes: 12 additions & 7 deletions frost-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(non_snake_case)]
// It's emitting false positives; see https://github.com/rust-lang/rust-clippy/issues/9413
#![allow(clippy::derive_partial_eq_without_eq)]
Expand All @@ -10,11 +11,15 @@
#![doc = include_str!("../README.md")]
#![doc = document_features::document_features!()]

use std::{
#[macro_use]
extern crate alloc;

use core::marker::PhantomData;

use alloc::{
collections::{BTreeMap, BTreeSet},
default::Default,
fmt::{self, Debug},
marker::PhantomData,
vec::Vec,
};

use derive_getters::Getters;
Expand Down Expand Up @@ -87,7 +92,7 @@ impl<C> Debug for Challenge<C>
where
C: Ciphersuite,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Secret")
.field(&hex::encode(<<C::Group as Group>::Field>::serialize(
&self.0,
Expand Down Expand Up @@ -115,7 +120,7 @@ fn challenge<C>(
where
C: Ciphersuite,
{
let mut preimage = vec![];
let mut preimage = Vec::new();

preimage.extend_from_slice(<C::Group>::serialize(R)?.as_ref());
preimage.extend_from_slice(<C::Group>::serialize(&verifying_key.to_element())?.as_ref());
Expand Down Expand Up @@ -409,7 +414,7 @@ where
verifying_key: &VerifyingKey<C>,
additional_prefix: &[u8],
) -> Result<Vec<(Identifier<C>, Vec<u8>)>, Error<C>> {
let mut binding_factor_input_prefix = vec![];
let mut binding_factor_input_prefix = Vec::new();

// The length of a serialized verifying key of the same cipersuite does
// not change between runs of the protocol, so we don't need to hash to
Expand All @@ -429,7 +434,7 @@ where
.signing_commitments()
.keys()
.map(|identifier| {
let mut binding_factor_input = vec![];
let mut binding_factor_input = Vec::new();

binding_factor_input.extend_from_slice(&binding_factor_input_prefix);
binding_factor_input.extend_from_slice(identifier.serialize().as_ref());
Expand Down
Loading

0 comments on commit 57c0b7b

Please sign in to comment.