diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..26a27bb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog +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.1.0] - 2020-01-13 +### Added +- Initial version diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..38c3e0d --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,12 @@ +Copyrights in the Sharks project are retained by their contributors. No +copyright assignment is required to contribute to the Sharks project. + +For full authorship information, see the version control history. + +Except as otherwise noted (below and/or in individual files), Sharks is +licensed under the Apache License, Version 2.0 or + or the MIT license + or , at your option. + +The Sharks project includes code from the Rust project +published under these same licenses. diff --git a/Cargo.toml b/Cargo.toml index 9a0f735..fe55c2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "sharks" version = "0.1.0" authors = ["Aitor Ruano "] -description = "Fast, secure and minimal Shamir's Secret Sharing library crate" +description = "Fast, smanll and secure Shamir's Secret Sharing library crate" homepage = "https://github.com/c0dearm/sharks" repository = "https://github.com/c0dearm/sharks" readme = "README.md" diff --git a/README.md b/README.md index e69de29..819a4c8 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,65 @@ +# Sharks + +Fast, small and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate + +Documentation: +- [API reference (docs.rs)](https://docs.rs/sharks) + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +sharks = "0.1" +``` + +To get started using Sharks, see the [Rust docs](https://docs.rs/sharks) + +## Features + +### Developer friendly +The API is simple and to the point, with minimal configuration. + +### Fast and small +The code is as idiomatic and clean as possible, with minimum external dependencies. + +### Secure by design +The implementation forbids the user to choose parameters that would result in an insecure application, +like generating more shares than what's allowed by the finite field length. + +## Limitations + +Currently only finite fields with modulus up to 128 bits (12th Mersenne prime) are supported. This means: +- Only up to `2^128` shares can be generated. +- Maximum secret length is 128 bits. + +This is imposed by the Rust maximum unsigned integer length, which is `u128`. +Going around this limitation would mean using crates like `num-bigint` in most of the computations, reducing performance drastically. + +## Testing + +This crate contains both unit and benchmark tests (as well as the examples included in the docs). +You can run them with `cargo test` and `cargo bench`. + +### Benchmark results [min mean max] + +| CPU | obtain_shares_iterator | step_shares_iterator | recover_secret | +| ----------------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | +| Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz | [14.023 us 14.087 us 14.146 us] | [413.19 us 414.90 us 416.60 us] | [24.978 ms 25.094 ms 25.226 ms] | + +# Contributing + +If you find a vulnerability, bug or would like a new feature, [open a new issue](https://github.com/c0dearm/sharks/issues/new). + +To introduce your changes into the codebase, submit a Pull Request. + +Many thanks! + +# License + +Sharks is distributed under the terms of both the MIT license and the +Apache License (Version 2.0). + +See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT), and +[COPYRIGHT](COPYRIGHT) for details. diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index 203557c..5416f44 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -1,4 +1,3 @@ - use std::collections::HashMap; use criterion::{black_box, criterion_group, criterion_main, Criterion}; @@ -8,15 +7,19 @@ fn secret_shares_generation(c: &mut Criterion) { let shamir = SecretShares::new(1000, 128).unwrap(); let mut iter = shamir.iter_shares(12345).unwrap(); - c.bench_function("obtain_shares_iterator", |b| b.iter(|| shamir.iter_shares(black_box(12345)))); + c.bench_function("obtain_shares_iterator", |b| { + b.iter(|| shamir.iter_shares(black_box(12345))) + }); c.bench_function("step_shares_iterator", |b| b.iter(|| iter.next())); } fn secret_from_shares(c: &mut Criterion) { let shamir = SecretShares::new(10, 128).unwrap(); - let shares: HashMap = shamir.iter_shares(12345).unwrap().take(100).collect(); + let shares: HashMap = shamir.iter_shares(12345).unwrap().take(100).collect(); - c.bench_function("recover_secret", |b| b.iter(|| shamir.secret_from(black_box(&shares)))); + c.bench_function("recover_secret", |b| { + b.iter(|| shamir.secret_from(black_box(&shares))) + }); } criterion_group!(benches, secret_shares_generation, secret_from_shares); diff --git a/src/lib.rs b/src/lib.rs index c6fc799..f521b3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,20 @@ +//! Fast, small and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate +//! +//! # Usage example +//! ``` +//! // Configure algorithm with minimum 3 shares to recover secret and security level 12 +//! let shamir = sharks::SecretShares::new(3, 12).unwrap(); +//! // Generate 3 shares for the 12345 secret +//! let shares = shamir.iter_shares(12345).unwrap().take(3).collect(); +//! // Recover the secret from the shares +//! let secret = shamir.secret_from(&shares).unwrap(); +//! assert_eq!(secret, 12345); +//! ``` + use std::collections::HashMap; -mod mersenne; mod math; +mod mersenne; /// Generate new [Shamir's secret shares](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) or recover secrets from them. pub struct SecretShares { @@ -10,14 +23,13 @@ pub struct SecretShares { } impl SecretShares { - /// Returns a result containing a`SecretShares` instance if parameters are reasonable. - /// + /// /// `security_level` is the index of the [Mersenne prime](https://en.wikipedia.org/wiki/Mersenne_prime) to use as the finite field prime modulo (the higher the more secure, but slower). /// Currently, only up to 12 is supported (`p=127, Mp = 2^127 - 1`). - /// + /// /// If `min_shares` is larger or equal to the Mersenne prime an error is returned, as this configuration would generate insecure shares. - /// + /// /// Example, create an instance with minimum 3 shares to recover a secret and 128 bits of security: /// ``` /// let shamir = sharks::SecretShares::new(3, 12); @@ -36,7 +48,7 @@ impl SecretShares { } /// Given a `secret` returns a result with an iterator which generates shares `(x, f(x))` for x from [1, p). - /// + /// /// If `secret` is larger or equal than the Mersenne prime an error is returned, as it would be irrecoverable. /// /// Example, generate 10 shares for secret `12345`: @@ -55,9 +67,9 @@ impl SecretShares { } /// Given a set of distinct `shares`, returns a result with the recovered secret. - /// + /// /// If the number of `shares` is less than the number of minimum shares an error is returned as the secret is irrecoverable. - /// + /// /// Example, recover the `12345` secret: /// ``` /// let shamir = sharks::SecretShares::new(3, 12).unwrap(); diff --git a/src/math.rs b/src/math.rs index 2c0a0ba..922a9eb 100644 --- a/src/math.rs +++ b/src/math.rs @@ -1,46 +1,60 @@ +// A module that contains necessary algorithms to compute Shamir's shares and recover secrets + use std::collections::HashMap; use num_bigint::{BigInt, BigUint}; -use num_traits::Zero; use num_traits::cast::ToPrimitive; +use num_traits::Zero; use rand::distributions::{Distribution, Uniform}; -/// A module that contains necessary algorithms to compute Shamir's shares and recover secrets - -/// Computes `num/(num - b) mod p`, a necessary step to compute the [root of the Lagrange polynomial](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing#Computationally_efficient_approach). -/// To find the modulo multiplicative inverse of `num - b` the [Extended Euclidean Algorithm](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse#Computation) is used. +// Computes `num/(num - b) mod p`, a necessary step to compute the [root of the Lagrange polynomial](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing#Computationally_efficient_approach). +// To find the modulo multiplicative inverse of `num - b` the [Extended Euclidean Algorithm](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse#Computation) is used. fn div_diff_mod(num: &u128, b: &u128, p: u128) -> u128 { - let (mut m, mut x, mut inv, mut den) = (p, 0i128, 1i128, if num < b { p - (b - num) } else { num - b }); + let (mut m, mut x, mut inv, mut den) = ( + p, + 0i128, + 1i128, + if num < b { p - (b - num) } else { num - b }, + ); while den > 1 { - inv -= ((den/m) as i128)*x; + inv -= ((den / m) as i128) * x; den %= m; std::mem::swap(&mut den, &mut m); std::mem::swap(&mut x, &mut inv); } - + let mut res = BigInt::from(inv); - if inv < 0 { res += p } - - (num*res % p).to_u128().unwrap() + if inv < 0 { + res += p + } + + (num * res % p).to_u128().unwrap() } -/// Finds the [root of the Lagrange polynomial](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing#Computationally_efficient_approach). +// Finds the [root of the Lagrange polynomial](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing#Computationally_efficient_approach). pub fn lagrange_root(points: &HashMap, p: u128) -> u128 { - (points.iter().enumerate() - .map(|(j, (xj, yj))| - points.iter().enumerate().filter(|(m, _)| *m != j) - .map(|(_, (xm, _))| - div_diff_mod(xm, xj, p) - ) - .product::()*yj % p - ) - .sum::() % p) - .to_u128().unwrap() + (points + .iter() + .enumerate() + .map(|(j, (xj, yj))| { + points + .iter() + .enumerate() + .filter(|(m, _)| *m != j) + .map(|(_, (xm, _))| div_diff_mod(xm, xj, p)) + .product::() + * yj + % p + }) + .sum::() + % p) + .to_u128() + .unwrap() } -/// Generates `k` polynomial coefficients, being the last one `s` and the others randomly generated between `[1, p)`. -/// Coefficient degrees go from higher to lower in the returned vector order. +// Generates `k` polynomial coefficients, being the last one `s` and the others randomly generated between `[1, p)`. +// Coefficient degrees go from higher to lower in the returned vector order. pub fn compute_coeffs(s: u128, k: usize, p: u128) -> (u128, Vec) { let mut coeffs = Vec::with_capacity(k); let between = Uniform::new(1, p); @@ -54,15 +68,24 @@ pub fn compute_coeffs(s: u128, k: usize, p: u128) -> (u128, Vec) { (p, coeffs) } -/// Given a set of polynomial coefficients `coeffs` and a modulus `p`, returns an iterator that computes a `(x, f(x) mod p)` point -/// on each iteration. The iterator starts for `x = 1` and ends at `x = p-1`. +// Given a set of polynomial coefficients `coeffs` and a modulus `p`, returns an iterator that computes a `(x, f(x) mod p)` point +// on each iteration. The iterator starts for `x = 1` and ends at `x = p-1`. pub fn get_evaluator(coeffs: Vec, p: u128) -> impl Iterator { - (1..p).map(move |x| (x, coeffs.iter().fold(BigUint::zero(), |acc, c| (acc*x + c) % p).to_u128().unwrap())) + (1..p).map(move |x| { + ( + x, + coeffs + .iter() + .fold(BigUint::zero(), |acc, c| (acc * x + c) % p) + .to_u128() + .unwrap(), + ) + }) } #[cfg(test)] mod tests { - use super::{div_diff_mod, lagrange_root, compute_coeffs, get_evaluator}; + use super::{compute_coeffs, div_diff_mod, get_evaluator, lagrange_root}; #[test] fn div_diff_mod_works() { @@ -75,12 +98,12 @@ mod tests { #[test] fn lagrange_root_works() { - let iter = get_evaluator(vec![3,2,1], 7); + let iter = get_evaluator(vec![3, 2, 1], 7); let values = iter.take(3).collect(); let root = lagrange_root(&values, 7); assert_eq!(root, 1); - let iter = get_evaluator(vec![3,2,5], 7); + let iter = get_evaluator(vec![3, 2, 5], 7); let values = iter.take(3).collect(); let root = lagrange_root(&values, 7); assert_eq!(root, 5); @@ -96,8 +119,8 @@ mod tests { #[test] fn evaluator_works() { - let iter = get_evaluator(vec![3,2,5], 7); + let iter = get_evaluator(vec![3, 2, 5], 7); let values: Vec<_> = iter.take(2).collect(); - assert_eq!(values, vec![(1,3), (2,0)]); + assert_eq!(values, vec![(1, 3), (2, 0)]); } } diff --git a/src/mersenne.rs b/src/mersenne.rs index 97ea3cd..233db9a 100644 --- a/src/mersenne.rs +++ b/src/mersenne.rs @@ -1,16 +1,3 @@ -/// A table containing the exponents of found [Mersenne primes](https://en.wikipedia.org/wiki/Mersenne_prime) -/// To be used as finite field modulo for shares and secret recovery computation -pub const EXPONENTS: [u32; 12] = [ - 2, - 3, - 5, - 7, - 13, - 17, - 19, - 31, - 61, - 89, - 107, - 127 -]; +// A table containing the exponents of found [Mersenne primes](https://en.wikipedia.org/wiki/Mersenne_prime) +// To be used as finite field modulo for shares and secret recovery computation +pub const EXPONENTS: [u32; 12] = [2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127];