Support no_std (#13)

* feat: support no_std

* simplify rng usage, update tests, upgrade version to 0.4

* fix workflow no-std test

* unignore doc-tests

* refactor tests

Co-authored-by: Aitor Ruano <codearm@pm.me>
This commit is contained in:
Kitsu 2020-04-02 14:45:01 +03:00 committed by GitHub
parent eb05d00080
commit 31c8cb7650
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 139 additions and 28 deletions

View File

@ -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

View File

@ -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

View File

@ -1,6 +1,6 @@
[package]
name = "sharks"
version = "0.3.3"
version = "0.4.0"
authors = ["Aitor Ruano <codearm@pm.me>"]
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"

View File

@ -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)

View File

@ -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() {

View File

@ -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<Share> = 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<Share> = 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<Share> = 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<Share> = dealer.take(3).collect();
pub fn dealer_rng<R: rand::Rng>(
&self,
secret: &[u8],
rng: &mut R,
) -> impl Iterator<Item = Share> {
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<Share> = dealer.take(3).collect();
#[cfg(feature = "std")]
pub fn dealer(&self, secret: &[u8]) -> impl Iterator<Item = Share> {
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<Share> = sharks.dealer(&[1]).take(3).collect();
/// # let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]);
/// # let mut shares: Vec<Share> = 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<Item = Share> {
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<Item = Share> {
self.dealer(secret)
}
}
#[test]
fn test_insufficient_shares_err() {
let sharks = Sharks(255);
let dealer = sharks.dealer(&[1]);
let shares: Vec<Share> = dealer.take(254).collect();
let shares: Vec<Share> = 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<Share> = dealer.take(255).collect();
let mut shares: Vec<Share> = 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<Share> = dealer.take(255).collect();
let shares: Vec<Share> = sharks.make_shares(&[1, 2, 3, 4]).take(255).collect();
let secret = sharks.recover(&shares).unwrap();
assert_eq!(secret, vec![1, 2, 3, 4]);
}

View File

@ -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<u8> {
// 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<GF256> {
pub fn random_polynomial<R: rand::Rng>(s: GF256, k: u8, rng: &mut R) -> Vec<GF256> {
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<Vec<GF256>>) -> impl Iterator<Item = Share> {
#[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<Share> = iter.take(10).collect();
let root = interpolate(&shares);

View File

@ -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<u8>) {}
/// # fn ask_shares() -> Vec<Vec<u8>> {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() {