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

add no-std support #621

Merged
merged 10 commits into from
Jun 20, 2024
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
Loading