use std::fmt;
use ring::rand::{SecureRandom, SystemRandom};
use errors::*;
use gf256::Gf256;
use dss::random::{random_bytes, random_bytes_count, MAX_MESSAGE_SIZE};
use share::validation::{validate_share_count, validate_shares};
use lagrange;
use super::AccessStructure;
use super::share::*;
use super::encode::encode_secret;
const MAX_SECRET_SIZE: usize = MAX_MESSAGE_SIZE;
#[allow(missing_debug_implementations)]
pub(crate) struct ThSS {
random: Box<SecureRandom>,
}
impl fmt::Debug for ThSS {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ThSS")
}
}
impl Default for ThSS {
fn default() -> Self {
Self::new(Box::new(SystemRandom::new()))
}
}
impl ThSS {
pub fn new(random: Box<SecureRandom>) -> Self {
Self { random }
}
pub fn split_secret(
&self,
threshold: u8,
shares_count: u8,
secret: &[u8],
metadata: &Option<MetaData>,
) -> Result<Vec<Share>> {
let (threshold, shares_count) = validate_share_count(threshold, shares_count)?;
let secret_len = secret.len();
if secret_len == 0 {
bail!(ErrorKind::EmptySecret);
}
if secret_len > MAX_SECRET_SIZE {
bail!(ErrorKind::SecretTooBig(secret_len, MAX_SECRET_SIZE));
}
let rands_len = random_bytes_count(threshold, secret_len);
let rands = random_bytes(self.random.as_ref(), rands_len)?;
let shares = (1..shares_count + 1)
.map(|id| {
let data = encode_secret(secret, threshold, id, &rands);
Share {
id,
threshold,
shares_count,
data,
metadata: metadata.clone(),
}
})
.collect();
Ok(shares)
}
pub fn recover_secret(
&self,
shares: &[Share],
) -> Result<(Vec<u8>, AccessStructure, Option<MetaData>)> {
let (threshold, shares) = validate_shares(shares.to_vec())?;
let cypher_len = shares[0].data.len();
let polys = (0..cypher_len)
.map(|i| {
let points = shares
.iter()
.take(threshold as usize)
.map(|share| (Gf256::from_byte(share.id), Gf256::from_byte(share.data[i])))
.collect::<Vec<_>>();
lagrange::interpolate(&points)
})
.collect::<Vec<_>>();
for (i, poly) in polys.iter().enumerate() {
let remaining_shares = shares.iter().enumerate().skip(threshold as usize);
for (u, share) in remaining_shares {
let value = poly.evaluate_at(Gf256::from_byte(u as u8 + 1)).to_byte();
if value != share.data[i] {
bail!(ErrorKind::InconsistentShares);
}
}
}
let metadata = shares[0].metadata.clone();
let secret = polys
.iter()
.map(|p| p.evaluate_at_zero().to_byte())
.collect();
let access_structure = AccessStructure {
threshold: threshold,
shares_count: shares.first().unwrap().shares_count,
};
Ok((secret, access_structure, metadata))
}
}