diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e3d884b..79bf87c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,7 +17,7 @@ jobs: with: command: check - test: + test-std: name: Test runs-on: ubuntu-latest steps: @@ -31,6 +31,21 @@ jobs: with: command: test + test-nostd: + name: Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --no-default-features + format: name: Format runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index f43d7d1..e6f51a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.4.0] - 2020-04-02 +### Added +- It is now possible to compile without `std` with `--no-default-features` + ## [0.3.3] - 2020-03-23 ### Changed - Fix codecov badge diff --git a/Cargo.toml b/Cargo.toml index 30c06c8..32df553 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sharks" -version = "0.3.3" +version = "0.4.0" authors = ["Aitor Ruano "] description = "Fast, small and secure Shamir's Secret Sharing library crate" homepage = "https://github.com/c0dearm/sharks" @@ -16,12 +16,17 @@ maintenance = { status = "actively-developed" } codecov = { repository = "c0dearm/sharks" } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["std"] +std = ["rand/std"] [dependencies] -rand = "0.7" +rand = { version = "0.7", default-features = false } +hashbrown = "0.7" [dev-dependencies] criterion = "0.3" +rand_chacha = "0.2" [[bench]] name = "benchmarks" diff --git a/README.md b/README.md index 48fe603..4726743 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,14 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -sharks = "0.3" +sharks = "0.4" +``` + +If your environment doesn't support `std`: + +```toml +[dependencies] +sharks = { version = "0.4", default-features = false } ``` To get started using Sharks, see the [Rust docs](https://docs.rs/sharks) diff --git a/src/field.rs b/src/field.rs index aa2d7eb..d707947 100644 --- a/src/field.rs +++ b/src/field.rs @@ -1,8 +1,8 @@ // Basic operations overrided for the Galois Field 256 (2**8) // Uses pre-calculated tables for 0x11d primitive polynomial (x**8 + x**4 + x**3 + x**2 + 1) -use std::iter::{Product, Sum}; -use std::ops::{Add, Div, Mul, Sub}; +use core::iter::{Product, Sum}; +use core::ops::{Add, Div, Mul, Sub}; const LOG_TABLE: [u8; 256] = [ 0x00, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b, @@ -126,6 +126,7 @@ impl Product for GF256 { #[cfg(test)] mod tests { use super::{EXP_TABLE, GF256, LOG_TABLE}; + use alloc::vec; #[test] fn add_works() { diff --git a/src/lib.rs b/src/lib.rs index a9d673e..45bfa73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,25 +1,49 @@ //! Fast, small and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate //! -//! Usage example: +//! Usage example (std): //! ``` //! use sharks::{ Sharks, Share }; //! //! // Set a minimum threshold of 10 shares //! let sharks = Sharks(10); //! // Obtain an iterator over the shares for secret [1, 2, 3, 4] +//! # #[cfg(feature = "std")] +//! # { //! let dealer = sharks.dealer(&[1, 2, 3, 4]); //! // Get 10 shares //! let shares: Vec = dealer.take(10).collect(); //! // Recover the original secret! //! let secret = sharks.recover(shares.as_slice()).unwrap(); //! assert_eq!(secret, vec![1, 2, 3, 4]); +//! # } //! ``` +//! +//! Usage example (no std): +//! ``` +//! use sharks::{ Sharks, Share }; +//! use rand_chacha::rand_core::SeedableRng; +//! +//! // Set a minimum threshold of 10 shares +//! let sharks = Sharks(10); +//! // Obtain an iterator over the shares for secret [1, 2, 3, 4] +//! let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); +//! let dealer = sharks.dealer_rng(&[1, 2, 3, 4], &mut rng); +//! // Get 10 shares +//! let shares: Vec = dealer.take(10).collect(); +//! // Recover the original secret! +//! let secret = sharks.recover(shares.as_slice()).unwrap(); +//! assert_eq!(secret, vec![1, 2, 3, 4]); +//! ``` +#![no_std] mod field; mod math; mod share; -use std::collections::HashSet; +extern crate alloc; + +use alloc::vec::Vec; +use hashbrown::HashSet; use field::GF256; pub use share::Share; @@ -33,16 +57,50 @@ pub use share::Share; /// // Set a minimum threshold of 10 shares /// let sharks = Sharks(10); /// // Obtain an iterator over the shares for secret [1, 2, 3, 4] +/// # #[cfg(feature = "std")] +/// # { /// let dealer = sharks.dealer(&[1, 2, 3, 4]); /// // Get 10 shares /// let shares: Vec = dealer.take(10).collect(); /// // Recover the original secret! /// let secret = sharks.recover(shares.as_slice()).unwrap(); /// assert_eq!(secret, vec![1, 2, 3, 4]); +/// # } /// ``` pub struct Sharks(pub u8); impl Sharks { + /// This method is useful when `std` is not available. For typical usage + /// see the `dealer` method. + /// + /// Given a `secret` byte slice, returns an `Iterator` along new shares. + /// The maximum number of shares that can be generated is 256. + /// A random number generator has to be provided. + /// + /// Example: + /// ``` + /// # use sharks::{ Sharks, Share }; + /// # use rand_chacha::rand_core::SeedableRng; + /// # let sharks = Sharks(3); + /// // Obtain an iterator over the shares for secret [1, 2] + /// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); + /// let dealer = sharks.dealer_rng(&[1, 2], &mut rng); + /// // Get 3 shares + /// let shares: Vec = dealer.take(3).collect(); + pub fn dealer_rng( + &self, + secret: &[u8], + rng: &mut R, + ) -> impl Iterator { + let mut polys = Vec::with_capacity(secret.len()); + + for chunk in secret { + polys.push(math::random_polynomial(GF256(*chunk), self.0, rng)) + } + + math::get_evaluator(polys) + } + /// Given a `secret` byte slice, returns an `Iterator` along new shares. /// The maximum number of shares that can be generated is 256. /// @@ -54,14 +112,10 @@ impl Sharks { /// let dealer = sharks.dealer(&[1, 2]); /// // Get 3 shares /// let shares: Vec = dealer.take(3).collect(); + #[cfg(feature = "std")] pub fn dealer(&self, secret: &[u8]) -> impl Iterator { - let mut polys = Vec::with_capacity(secret.len()); - - for chunk in secret { - polys.push(math::random_polynomial(GF256(*chunk), self.0)) - } - - math::get_evaluator(polys) + let mut rng = rand::thread_rng(); + self.dealer_rng(secret, &mut rng) } /// Given an iterable collection of shares, recovers the original secret. @@ -71,8 +125,10 @@ impl Sharks { /// Example: /// ``` /// # use sharks::{ Sharks, Share }; + /// # use rand_chacha::rand_core::SeedableRng; /// # let sharks = Sharks(3); - /// # let mut shares: Vec = sharks.dealer(&[1]).take(3).collect(); + /// # let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); + /// # let mut shares: Vec = sharks.dealer_rng(&[1], &mut rng).take(3).collect(); /// // Recover original secret from shares /// let mut secret = sharks.recover(&shares); /// // Secret correctly recovered @@ -111,12 +167,27 @@ impl Sharks { #[cfg(test)] mod tests { use super::{Share, Sharks}; + use alloc::{vec, vec::Vec}; + + impl Sharks { + #[cfg(not(feature = "std"))] + fn make_shares(&self, secret: &[u8]) -> impl Iterator { + use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + + let mut rng = ChaCha8Rng::from_seed([0x90; 32]); + self.dealer_rng(secret, &mut rng) + } + + #[cfg(feature = "std")] + fn make_shares(&self, secret: &[u8]) -> impl Iterator { + self.dealer(secret) + } + } #[test] fn test_insufficient_shares_err() { let sharks = Sharks(255); - let dealer = sharks.dealer(&[1]); - let shares: Vec = dealer.take(254).collect(); + let shares: Vec = sharks.make_shares(&[1]).take(254).collect(); let secret = sharks.recover(&shares); assert!(secret.is_err()); } @@ -124,8 +195,7 @@ mod tests { #[test] fn test_duplicate_shares_err() { let sharks = Sharks(255); - let dealer = sharks.dealer(&[1]); - let mut shares: Vec = dealer.take(255).collect(); + let mut shares: Vec = sharks.make_shares(&[1]).take(255).collect(); shares[1] = Share { x: shares[0].x, y: shares[0].y.clone(), @@ -137,8 +207,7 @@ mod tests { #[test] fn test_integration_works() { let sharks = Sharks(255); - let dealer = sharks.dealer(&[1, 2, 3, 4]); - let shares: Vec = dealer.take(255).collect(); + let shares: Vec = sharks.make_shares(&[1, 2, 3, 4]).take(255).collect(); let secret = sharks.recover(&shares).unwrap(); assert_eq!(secret, vec![1, 2, 3, 4]); } diff --git a/src/math.rs b/src/math.rs index 187709d..7279018 100644 --- a/src/math.rs +++ b/src/math.rs @@ -1,5 +1,7 @@ // A module which contains necessary algorithms to compute Shamir's shares and recover secrets +use alloc::vec::Vec; + use rand::distributions::{Distribution, Uniform}; use super::field::GF256; @@ -30,14 +32,13 @@ pub fn interpolate(shares: &[Share]) -> Vec { // Generates `k` polynomial coefficients, being the last one `s` and the others randomly generated between `[1, 255]`. // Coefficient degrees go from higher to lower in the returned vector order. -pub fn random_polynomial(s: GF256, k: u8) -> Vec { +pub fn random_polynomial(s: GF256, k: u8, rng: &mut R) -> Vec { let k = k as usize; let mut poly = Vec::with_capacity(k); let between = Uniform::new_inclusive(1, 255); - let mut rng = rand::thread_rng(); for _ in 1..k { - poly.push(GF256(between.sample(&mut rng))); + poly.push(GF256(between.sample(rng))); } poly.push(s); @@ -61,10 +62,13 @@ pub fn get_evaluator(polys: Vec>) -> impl Iterator { #[cfg(test)] mod tests { use super::{get_evaluator, interpolate, random_polynomial, Share, GF256}; + use alloc::{vec, vec::Vec}; + use rand_chacha::rand_core::SeedableRng; #[test] fn random_polynomial_works() { - let poly = random_polynomial(GF256(1), 3); + let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); + let poly = random_polynomial(GF256(1), 3, &mut rng); assert_eq!(poly.len(), 3); assert_eq!(poly[2], GF256(1)); } @@ -81,7 +85,8 @@ mod tests { #[test] fn interpolate_works() { - let poly = random_polynomial(GF256(185), 10); + let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); + let poly = random_polynomial(GF256(185), 10, &mut rng); let iter = get_evaluator(vec![poly]); let shares: Vec = iter.take(10).collect(); let root = interpolate(&shares); diff --git a/src/share.rs b/src/share.rs index c6b861b..31e8b61 100644 --- a/src/share.rs +++ b/src/share.rs @@ -1,3 +1,5 @@ +use alloc::vec::Vec; + use super::field::GF256; /// A share used to reconstruct the secret. Can be serialized to and from a byte array. @@ -5,12 +7,14 @@ use super::field::GF256; /// Usage example: /// ``` /// use sharks::{Sharks, Share}; +/// # use rand_chacha::rand_core::SeedableRng; /// # fn send_to_printer(_: Vec) {} /// # fn ask_shares() -> Vec> {vec![vec![1, 2], vec![2, 3], vec![3, 4]]} /// /// // Transmit the share bytes to a printer /// let sharks = Sharks(3); -/// let dealer = sharks.dealer(&[1, 2, 3]); +/// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); +/// let dealer = sharks.dealer_rng(&[1, 2, 3], &mut rng); /// /// // Get 5 shares and print paper keys /// for s in dealer.take(5) { @@ -49,6 +53,7 @@ impl From<&[u8]> for Share { #[cfg(test)] mod tests { use super::{Share, GF256}; + use alloc::{vec, vec::Vec}; #[test] fn vec_from_share_works() {