added share serialisation and created Share struct
This commit is contained in:
parent
5344dda94b
commit
58e735d17e
35
src/lib.rs
35
src/lib.rs
|
@ -2,14 +2,14 @@
|
|||
//!
|
||||
//! 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();
|
||||
//! assert_eq!(secret, vec![1, 2, 3, 4]);
|
||||
|
@ -17,23 +17,23 @@
|
|||
|
||||
mod field;
|
||||
mod math;
|
||||
|
||||
use std::collections::HashMap;
|
||||
mod share;
|
||||
|
||||
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();
|
||||
/// assert_eq!(secret, vec![1, 2, 3, 4]);
|
||||
|
@ -46,14 +46,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 {
|
||||
|
@ -69,19 +68,19 @@ impl Sharks {
|
|||
///
|
||||
/// 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> {
|
||||
pub fn recover(&self, shares: &[Share]) -> Result<Vec<u8>, &str> {
|
||||
if shares.len() < self.0 as usize {
|
||||
Err("Not enough shares to recover original secret")
|
||||
} else {
|
||||
|
@ -92,13 +91,13 @@ 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());
|
||||
}
|
||||
|
@ -107,7 +106,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]);
|
||||
}
|
||||
|
|
31
src/math.rs
31
src/math.rs
|
@ -1,29 +1,28 @@
|
|||
// 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();
|
||||
pub fn interpolate(shares: &[Share]) -> Vec<u8> {
|
||||
let n_chunks = shares[0].y.len();
|
||||
|
||||
(0..n_chunks)
|
||||
.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 +50,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 +76,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 +87,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,57 @@
|
|||
use crate::GF256;
|
||||
|
||||
/// A share used for to reconstruct the secret. Can be serialized to and from a byte array for transmission.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// # use std::borrow::Borrow;
|
||||
/// # fn send_to_printer(_: Vec<u8>) { }
|
||||
/// # fn ask_shares() -> Vec<Vec<u8>> { vec![] }
|
||||
///
|
||||
/// # use sharks::{ Sharks, Share };
|
||||
/// let sharks = Sharks(3);
|
||||
/// // Obtain an iterator over the shares for secret [1, 2]
|
||||
/// let dealer = sharks.dealer(&[1, 2, 3]);
|
||||
///
|
||||
/// # let mut shares: Vec<Vec<u8>> = Vec::with_capacity(5);
|
||||
/// // Get 5 shares and print paper keys
|
||||
/// for s in dealer.take(5) {
|
||||
/// println!("test");
|
||||
/// # shares.push(s.clone().into());
|
||||
/// send_to_printer(s.into());
|
||||
/// };
|
||||
///
|
||||
/// # let shares = vec![shares[0].clone(), shares[2].clone(), shares[4].clone()];
|
||||
///
|
||||
/// // Get 3 shares from users and get secret
|
||||
/// let shares_serialized: Vec<Vec<u8>> = ask_shares();
|
||||
/// # let shares_serialized = shares;
|
||||
///
|
||||
/// let shares: Vec<Share> = shares_serialized.iter().map(|s| s.as_slice().into()).collect();
|
||||
///
|
||||
/// let secret = sharks.recover(&shares).expect("we should have at leats 3 shares");
|
||||
///
|
||||
/// assert_eq!(secret, vec![1, 2, 3]);
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Share {
|
||||
pub x: GF256,
|
||||
pub y: Vec<GF256>,
|
||||
}
|
||||
|
||||
impl From<Share> for Vec<u8> {
|
||||
fn from(s: Share) -> Vec<u8> {
|
||||
let mut serialized: Vec<u8> = Vec::with_capacity(s.y.len() + 1);
|
||||
serialized.push(s.x.0);
|
||||
|
||||
serialized.append(&mut s.y.iter().map(|p| p.0).collect());
|
||||
serialized
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue