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:
parent
eb05d00080
commit
31c8cb7650
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() {
|
||||
|
|
101
src/lib.rs
101
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<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]);
|
||||
}
|
||||
|
|
15
src/math.rs
15
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<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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue