Written readme, improved documentation, added changelog and copyright

This commit is contained in:
Aitor Ruano 2020-01-13 11:42:59 +01:00
parent 90c7382c17
commit d6c7a6be9e
8 changed files with 172 additions and 61 deletions

9
CHANGELOG.md Normal file
View File

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

12
COPYRIGHT Normal file
View File

@ -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 <LICENSE-APACHE> or
<http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
<LICENSE-MIT> or <http://opensource.org/licenses/MIT>, at your option.
The Sharks project includes code from the Rust project
published under these same licenses.

View File

@ -2,7 +2,7 @@
name = "sharks" name = "sharks"
version = "0.1.0" version = "0.1.0"
authors = ["Aitor Ruano <codearm@pm.me>"] authors = ["Aitor Ruano <codearm@pm.me>"]
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" homepage = "https://github.com/c0dearm/sharks"
repository = "https://github.com/c0dearm/sharks" repository = "https://github.com/c0dearm/sharks"
readme = "README.md" readme = "README.md"

View File

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

View File

@ -1,4 +1,3 @@
use std::collections::HashMap; use std::collections::HashMap;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; 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 shamir = SecretShares::new(1000, 128).unwrap();
let mut iter = shamir.iter_shares(12345).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())); c.bench_function("step_shares_iterator", |b| b.iter(|| iter.next()));
} }
fn secret_from_shares(c: &mut Criterion) { fn secret_from_shares(c: &mut Criterion) {
let shamir = SecretShares::new(10, 128).unwrap(); let shamir = SecretShares::new(10, 128).unwrap();
let shares: HashMap<u128,u128> = shamir.iter_shares(12345).unwrap().take(100).collect(); let shares: HashMap<u128, u128> = 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); criterion_group!(benches, secret_shares_generation, secret_from_shares);

View File

@ -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; use std::collections::HashMap;
mod mersenne;
mod math; mod math;
mod mersenne;
/// Generate new [Shamir's secret shares](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) or recover secrets from them. /// Generate new [Shamir's secret shares](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) or recover secrets from them.
pub struct SecretShares { pub struct SecretShares {
@ -10,14 +23,13 @@ pub struct SecretShares {
} }
impl SecretShares { impl SecretShares {
/// Returns a result containing a`SecretShares` instance if parameters are reasonable. /// 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). /// `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`). /// 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. /// 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: /// Example, create an instance with minimum 3 shares to recover a secret and 128 bits of security:
/// ``` /// ```
/// let shamir = sharks::SecretShares::new(3, 12); /// 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). /// 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. /// 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`: /// 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. /// 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. /// 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: /// Example, recover the `12345` secret:
/// ``` /// ```
/// let shamir = sharks::SecretShares::new(3, 12).unwrap(); /// let shamir = sharks::SecretShares::new(3, 12).unwrap();

View File

@ -1,46 +1,60 @@
// A module that contains necessary algorithms to compute Shamir's shares and recover secrets
use std::collections::HashMap; use std::collections::HashMap;
use num_bigint::{BigInt, BigUint}; use num_bigint::{BigInt, BigUint};
use num_traits::Zero;
use num_traits::cast::ToPrimitive; use num_traits::cast::ToPrimitive;
use num_traits::Zero;
use rand::distributions::{Distribution, Uniform}; 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 { 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 { while den > 1 {
inv -= ((den/m) as i128)*x; inv -= ((den / m) as i128) * x;
den %= m; den %= m;
std::mem::swap(&mut den, &mut m); std::mem::swap(&mut den, &mut m);
std::mem::swap(&mut x, &mut inv); std::mem::swap(&mut x, &mut inv);
} }
let mut res = BigInt::from(inv); let mut res = BigInt::from(inv);
if inv < 0 { res += p } if inv < 0 {
res += p
(num*res % p).to_u128().unwrap() }
(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<u128, u128>, p: u128) -> u128 { pub fn lagrange_root(points: &HashMap<u128, u128>, p: u128) -> u128 {
(points.iter().enumerate() (points
.map(|(j, (xj, yj))| .iter()
points.iter().enumerate().filter(|(m, _)| *m != j) .enumerate()
.map(|(_, (xm, _))| .map(|(j, (xj, yj))| {
div_diff_mod(xm, xj, p) points
) .iter()
.product::<BigUint>()*yj % p .enumerate()
) .filter(|(m, _)| *m != j)
.sum::<BigUint>() % p) .map(|(_, (xm, _))| div_diff_mod(xm, xj, p))
.to_u128().unwrap() .product::<BigUint>()
* yj
% p
})
.sum::<BigUint>()
% p)
.to_u128()
.unwrap()
} }
/// Generates `k` polynomial coefficients, being the last one `s` and the others randomly generated between `[1, p)`. // 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. // Coefficient degrees go from higher to lower in the returned vector order.
pub fn compute_coeffs(s: u128, k: usize, p: u128) -> (u128, Vec<u128>) { pub fn compute_coeffs(s: u128, k: usize, p: u128) -> (u128, Vec<u128>) {
let mut coeffs = Vec::with_capacity(k); let mut coeffs = Vec::with_capacity(k);
let between = Uniform::new(1, p); let between = Uniform::new(1, p);
@ -54,15 +68,24 @@ pub fn compute_coeffs(s: u128, k: usize, p: u128) -> (u128, Vec<u128>) {
(p, coeffs) (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 // 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`. // on each iteration. The iterator starts for `x = 1` and ends at `x = p-1`.
pub fn get_evaluator(coeffs: Vec<u128>, p: u128) -> impl Iterator<Item = (u128, u128)> { pub fn get_evaluator(coeffs: Vec<u128>, p: u128) -> impl Iterator<Item = (u128, u128)> {
(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)] #[cfg(test)]
mod tests { 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] #[test]
fn div_diff_mod_works() { fn div_diff_mod_works() {
@ -75,12 +98,12 @@ mod tests {
#[test] #[test]
fn lagrange_root_works() { 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 values = iter.take(3).collect();
let root = lagrange_root(&values, 7); let root = lagrange_root(&values, 7);
assert_eq!(root, 1); 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 values = iter.take(3).collect();
let root = lagrange_root(&values, 7); let root = lagrange_root(&values, 7);
assert_eq!(root, 5); assert_eq!(root, 5);
@ -96,8 +119,8 @@ mod tests {
#[test] #[test]
fn evaluator_works() { 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(); let values: Vec<_> = iter.take(2).collect();
assert_eq!(values, vec![(1,3), (2,0)]); assert_eq!(values, vec![(1, 3), (2, 0)]);
} }
} }

View File

@ -1,16 +1,3 @@
/// A table containing the exponents of found [Mersenne primes](https://en.wikipedia.org/wiki/Mersenne_prime) // 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 // To be used as finite field modulo for shares and secret recovery computation
pub const EXPONENTS: [u32; 12] = [ pub const EXPONENTS: [u32; 12] = [2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127];
2,
3,
5,
7,
13,
17,
19,
31,
61,
89,
107,
127
];