Added Share Serialization and Created Share Struct (#2)
* Added share serialisation and created Share struct Co-authored-by: Aitor Ruano <45633475+c0dearm@users.noreply.github.com>
This commit is contained in:
parent
5344dda94b
commit
3489574840
|
@ -4,6 +4,13 @@ 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.3.0] - 2020-01-22
|
||||
### Added
|
||||
- Share struct which allows to convert from/to byte vectors
|
||||
|
||||
### Changed
|
||||
- Methods use the new Share struct, instead of (GF245, Vec<GF256>) tuples
|
||||
|
||||
## [0.2.0] - 2020-01-21
|
||||
### Added
|
||||
- Computations performed over GF256 (much faster)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "sharks"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = ["Aitor Ruano <codearm@pm.me>"]
|
||||
description = "Fast, small and secure Shamir's Secret Sharing library crate"
|
||||
homepage = "https://github.com/c0dearm/sharks"
|
||||
|
|
|
@ -45,9 +45,9 @@ You can run them with `cargo test` and `cargo bench`.
|
|||
|
||||
### Benchmark results [min mean max]
|
||||
|
||||
| CPU | obtain_shares_dealer | step_shares_dealer | recover_secret |
|
||||
| ----------------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- |
|
||||
| Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz | [1.4321 us 1.4339 us 1.4357 us] | [1.3385 ns 1.3456 ns 1.3552 ns] | [228.77 us 232.17 us 236.23 us] |
|
||||
| CPU | obtain_shares_dealer | step_shares_dealer | recover_secret | share_from_bytes | share_to_bytes |
|
||||
| ----------------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- |
|
||||
| Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz | [1.4321 us 1.4339 us 1.4357 us] | [1.3385 ns 1.3456 ns 1.3552 ns] | [228.77 us 232.17 us 236.23 us] | [24.688 ns 25.083 ns 25.551 ns] | [22.832 ns 22.910 ns 22.995 ns] |
|
||||
|
||||
# Contributing
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
use sharks::Sharks;
|
||||
use sharks::{Share, Sharks};
|
||||
|
||||
fn dealer(c: &mut Criterion) {
|
||||
let sharks = Sharks(255);
|
||||
|
@ -14,12 +14,26 @@ fn dealer(c: &mut Criterion) {
|
|||
|
||||
fn recover(c: &mut Criterion) {
|
||||
let sharks = Sharks(255);
|
||||
let shares = sharks.dealer(&[1]).take(255).collect();
|
||||
let shares: Vec<Share> = sharks.dealer(&[1]).take(255).collect();
|
||||
|
||||
c.bench_function("recover_secret", |b| {
|
||||
b.iter(|| sharks.recover(black_box(&shares)))
|
||||
b.iter(|| sharks.recover(black_box(shares.as_slice())))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, dealer, recover);
|
||||
fn share(c: &mut Criterion) {
|
||||
let bytes_vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
let bytes = bytes_vec.as_slice();
|
||||
let share = Share::from(bytes);
|
||||
|
||||
c.bench_function("share_from_bytes", |b| {
|
||||
b.iter(|| Share::from(black_box(bytes)))
|
||||
});
|
||||
|
||||
c.bench_function("share_to_bytes", |b| {
|
||||
b.iter(|| Vec::from(black_box(&share)))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, dealer, recover, share);
|
||||
criterion_main!(benches);
|
||||
|
|
60
src/lib.rs
60
src/lib.rs
|
@ -2,40 +2,42 @@
|
|||
//!
|
||||
//! Usage example:
|
||||
//! ```
|
||||
//! use sharks::Sharks;
|
||||
//! 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]
|
||||
//! let dealer = sharks.dealer(&[1, 2, 3, 4]);
|
||||
//! // Get 10 shares
|
||||
//! let shares = dealer.take(10).collect();
|
||||
//! let shares: Vec<Share> = dealer.take(10).collect();
|
||||
//! // Recover the original secret!
|
||||
//! let secret = sharks.recover(&shares).unwrap();
|
||||
//! let secret = sharks.recover(shares.as_slice()).unwrap();
|
||||
//! assert_eq!(secret, vec![1, 2, 3, 4]);
|
||||
//! ```
|
||||
|
||||
mod field;
|
||||
mod math;
|
||||
mod share;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use field::GF256;
|
||||
pub use share::Share;
|
||||
|
||||
/// Tuple struct which implements methods to generate shares and recover secrets over a 256 bits Galois Field.
|
||||
/// Its only parameter is the minimum shares threshold.
|
||||
///
|
||||
/// Usage example:
|
||||
/// ```
|
||||
/// # use sharks::Sharks;
|
||||
/// # 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]
|
||||
/// let dealer = sharks.dealer(&[1, 2, 3, 4]);
|
||||
/// // Get 10 shares
|
||||
/// let shares = dealer.take(10).collect();
|
||||
/// let shares: Vec<Share> = dealer.take(10).collect();
|
||||
/// // Recover the original secret!
|
||||
/// let secret = sharks.recover(&shares).unwrap();
|
||||
/// let secret = sharks.recover(shares.as_slice()).unwrap();
|
||||
/// assert_eq!(secret, vec![1, 2, 3, 4]);
|
||||
/// ```
|
||||
pub struct Sharks(pub u8);
|
||||
|
@ -46,14 +48,13 @@ impl Sharks {
|
|||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// # use std::collections::HashMap;
|
||||
/// # use sharks::Sharks;
|
||||
/// # use sharks::{ Sharks, Share };
|
||||
/// # let sharks = Sharks(3);
|
||||
/// // Obtain an iterator over the shares for secret [1, 2]
|
||||
/// let dealer = sharks.dealer(&[1, 2]);
|
||||
/// // Get 3 shares
|
||||
/// let shares: HashMap<_, _> = dealer.take(3).collect();
|
||||
pub fn dealer(&self, secret: &[u8]) -> impl Iterator<Item = (GF256, Vec<GF256>)> {
|
||||
/// let shares: Vec<Share> = dealer.take(3).collect();
|
||||
pub fn dealer(&self, secret: &[u8]) -> impl Iterator<Item = Share> {
|
||||
let mut polys = Vec::with_capacity(secret.len());
|
||||
|
||||
for chunk in secret {
|
||||
|
@ -63,26 +64,28 @@ impl Sharks {
|
|||
math::get_evaluator(polys)
|
||||
}
|
||||
|
||||
/// Given a `HashMap` of shares, recovers the original secret.
|
||||
/// If the number of shares is less than the minimum threshold an `Err` is returned,
|
||||
/// Given a slice of shares, recovers the original secret.
|
||||
/// If the number of distinct shares is less than the minimum threshold an `Err` is returned,
|
||||
/// otherwise an `Ok` containing the secret.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// # use sharks::Sharks;
|
||||
/// # use sharks::{ Sharks, Share };
|
||||
/// # let sharks = Sharks(3);
|
||||
/// # let mut shares = sharks.dealer(&[1]).take(3).collect();
|
||||
/// // Revover original secret from shares
|
||||
/// # let mut shares: Vec<Share> = sharks.dealer(&[1]).take(3).collect();
|
||||
/// // Recover original secret from shares
|
||||
/// let mut secret = sharks.recover(&shares);
|
||||
/// // Secret correctly recovered
|
||||
/// assert!(secret.is_ok());
|
||||
/// // Remove shares for demonstrastion purposes
|
||||
/// // Remove shares for demonstration purposes
|
||||
/// shares.clear();
|
||||
/// secret = sharks.recover(&shares);
|
||||
/// // Not enough shares to recover secret
|
||||
/// assert!(secret.is_err());
|
||||
pub fn recover(&self, shares: &HashMap<GF256, Vec<GF256>>) -> Result<Vec<u8>, &str> {
|
||||
if shares.len() < self.0 as usize {
|
||||
pub fn recover(&self, shares: &[Share]) -> Result<Vec<u8>, &str> {
|
||||
let shares_x: HashSet<u8> = shares.iter().map(|s| s.x.0).collect();
|
||||
|
||||
if shares_x.len() < self.0 as usize {
|
||||
Err("Not enough shares to recover original secret")
|
||||
} else {
|
||||
Ok(math::interpolate(shares))
|
||||
|
@ -92,13 +95,26 @@ impl Sharks {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Sharks, GF256};
|
||||
use super::{Share, Sharks};
|
||||
|
||||
#[test]
|
||||
fn test_insufficient_shares_err() {
|
||||
let sharks = Sharks(255);
|
||||
let dealer = sharks.dealer(&[1]);
|
||||
let shares = dealer.take(254).collect();
|
||||
let shares: Vec<Share> = dealer.take(254).collect();
|
||||
let secret = sharks.recover(&shares);
|
||||
assert!(secret.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duplicate_shares_err() {
|
||||
let sharks = Sharks(255);
|
||||
let dealer = sharks.dealer(&[1]);
|
||||
let mut shares: Vec<Share> = dealer.take(255).collect();
|
||||
shares[1] = Share {
|
||||
x: shares[0].x,
|
||||
y: shares[0].y.clone(),
|
||||
};
|
||||
let secret = sharks.recover(&shares);
|
||||
assert!(secret.is_err());
|
||||
}
|
||||
|
@ -107,7 +123,7 @@ mod tests {
|
|||
fn test_integration_works() {
|
||||
let sharks = Sharks(255);
|
||||
let dealer = sharks.dealer(&[1, 2, 3, 4]);
|
||||
let shares: std::collections::HashMap<GF256, Vec<GF256>> = dealer.take(255).collect();
|
||||
let shares: Vec<Share> = dealer.take(255).collect();
|
||||
let secret = sharks.recover(&shares).unwrap();
|
||||
assert_eq!(secret, vec![1, 2, 3, 4]);
|
||||
}
|
||||
|
|
33
src/math.rs
33
src/math.rs
|
@ -1,29 +1,26 @@
|
|||
// A module which contains necessary algorithms to compute Shamir's shares and recover secrets
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
|
||||
use super::field::GF256;
|
||||
use super::share::Share;
|
||||
|
||||
// Finds the [root of the Lagrange polynomial](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing#Computationally_efficient_approach).
|
||||
// The expected `shares` argument format is the same as the output by the `get_evaluator´ function.
|
||||
// Where each (key, value) pair corresponds to one share, where the key is the `x` and the value is a vector of `y`,
|
||||
// where each element corresponds to one of the secret's byte chunks.
|
||||
pub fn interpolate(shares: &HashMap<GF256, Vec<GF256>>) -> Vec<u8> {
|
||||
let n_chunks = shares.values().take(1).collect::<Vec<&Vec<GF256>>>()[0].len();
|
||||
|
||||
(0..n_chunks)
|
||||
pub fn interpolate(shares: &[Share]) -> Vec<u8> {
|
||||
(0..shares[0].y.len())
|
||||
.map(|s| {
|
||||
shares
|
||||
.iter()
|
||||
.map(|(x_i, y_i)| {
|
||||
.map(|s_i| {
|
||||
shares
|
||||
.keys()
|
||||
.filter(|x_j| *x_j != x_i)
|
||||
.map(|x_j| *x_j / (*x_j - *x_i))
|
||||
.iter()
|
||||
.filter(|s_j| s_j.x != s_i.x)
|
||||
.map(|s_j| s_j.x / (s_j.x - s_i.x))
|
||||
.product::<GF256>()
|
||||
* y_i[s]
|
||||
* s_i.y[s]
|
||||
})
|
||||
.sum::<GF256>()
|
||||
.0
|
||||
|
@ -51,21 +48,21 @@ pub fn random_polynomial(s: GF256, k: u8) -> Vec<GF256> {
|
|||
// Each item of the iterator is a tuple `(x, [f_1(x), f_2(x)..])` where eaxh `f_i` is the result for the ith polynomial.
|
||||
// Each polynomial corresponds to one byte chunk of the original secret.
|
||||
// The iterator will start at `x = 1` and end at `x = 255`.
|
||||
pub fn get_evaluator(polys: Vec<Vec<GF256>>) -> impl Iterator<Item = (GF256, Vec<GF256>)> {
|
||||
pub fn get_evaluator(polys: Vec<Vec<GF256>>) -> impl Iterator<Item = Share> {
|
||||
(1..=u8::max_value()).map(GF256).map(move |x| {
|
||||
(
|
||||
(Share {
|
||||
x,
|
||||
polys
|
||||
y: polys
|
||||
.iter()
|
||||
.map(|p| p.iter().fold(GF256(0), |acc, c| acc * x + *c))
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{get_evaluator, interpolate, random_polynomial, GF256};
|
||||
use super::{get_evaluator, interpolate, random_polynomial, Share, GF256};
|
||||
|
||||
#[test]
|
||||
fn random_polynomial_works() {
|
||||
|
@ -77,7 +74,7 @@ mod tests {
|
|||
#[test]
|
||||
fn evaluator_works() {
|
||||
let iter = get_evaluator(vec![vec![GF256(3), GF256(2), GF256(5)]]);
|
||||
let values: Vec<_> = iter.take(2).collect();
|
||||
let values: Vec<_> = iter.take(2).map(|s| (s.x, s.y)).collect();
|
||||
assert_eq!(
|
||||
values,
|
||||
vec![(GF256(1), vec![GF256(4)]), (GF256(2), vec![GF256(13)])]
|
||||
|
@ -88,7 +85,7 @@ mod tests {
|
|||
fn interpolate_works() {
|
||||
let poly = random_polynomial(GF256(185), 10);
|
||||
let iter = get_evaluator(vec![poly]);
|
||||
let shares = iter.take(10).collect();
|
||||
let shares: Vec<Share> = iter.take(10).collect();
|
||||
let root = interpolate(&shares);
|
||||
assert_eq!(root, vec![185]);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
use super::field::GF256;
|
||||
|
||||
/// A share used to reconstruct the secret. Can be serialized to and from a byte array.
|
||||
///
|
||||
/// Usage example:
|
||||
/// ```
|
||||
/// use sharks::{Sharks, Share};
|
||||
/// # 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]);
|
||||
///
|
||||
/// // Get 5 shares and print paper keys
|
||||
/// for s in dealer.take(5) {
|
||||
/// send_to_printer(Vec::from(&s));
|
||||
/// };
|
||||
///
|
||||
/// // Get share bytes from an external source and recover secret
|
||||
/// let shares_bytes: Vec<Vec<u8>> = ask_shares();
|
||||
/// let shares: Vec<Share> = shares_bytes.iter().map(|s| Share::from(s.as_slice())).collect();
|
||||
/// let secret = sharks.recover(&shares).unwrap();
|
||||
pub struct Share {
|
||||
pub x: GF256,
|
||||
pub y: Vec<GF256>,
|
||||
}
|
||||
|
||||
/// Obtains a byte vector from a `Share` instance
|
||||
impl From<&Share> for Vec<u8> {
|
||||
fn from(s: &Share) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(s.y.len() + 1);
|
||||
bytes.push(s.x.0);
|
||||
bytes.extend(s.y.iter().map(|p| p.0));
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtains a `Share` instance from a byte slice
|
||||
impl From<&[u8]> for Share {
|
||||
fn from(s: &[u8]) -> Share {
|
||||
let x = GF256(s[0]);
|
||||
let y = s[1..].iter().map(|p| GF256(*p)).collect();
|
||||
Share { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Share, GF256};
|
||||
|
||||
#[test]
|
||||
fn vec_from_share_works() {
|
||||
let share = Share {
|
||||
x: GF256(1),
|
||||
y: vec![GF256(2), GF256(3)],
|
||||
};
|
||||
let bytes = Vec::from(&share);
|
||||
assert_eq!(bytes, vec![1, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn share_from_u8_slice_works() {
|
||||
let bytes = [1, 2, 3];
|
||||
let share = Share::from(&bytes[..]);
|
||||
assert_eq!(share.x, GF256(1));
|
||||
assert_eq!(share.y, vec![GF256(2), GF256(3)]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue