Written readme, improved documentation, added changelog and copyright
This commit is contained in:
parent
90c7382c17
commit
d6c7a6be9e
|
@ -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
|
|
@ -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.
|
|
@ -2,7 +2,7 @@
|
|||
name = "sharks"
|
||||
version = "0.1.0"
|
||||
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"
|
||||
repository = "https://github.com/c0dearm/sharks"
|
||||
readme = "README.md"
|
||||
|
|
65
README.md
65
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.
|
|
@ -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<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);
|
||||
|
|
28
src/lib.rs
28
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();
|
||||
|
|
87
src/math.rs
87
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<u128, u128>, 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::<BigUint>()*yj % p
|
||||
)
|
||||
.sum::<BigUint>() % 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::<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)`.
|
||||
/// 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<u128>) {
|
||||
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<u128>) {
|
|||
(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<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)]
|
||||
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)]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
Loading…
Reference in New Issue