mirror of
https://github.com/mii443/RustySecrets.git
synced 2025-08-22 16:25:32 +00:00
Adding share_num field to errors.
This commit is contained in:
48
Cargo.lock
generated
48
Cargo.lock
generated
@ -2,20 +2,20 @@
|
||||
name = "rusty_secrets"
|
||||
version = "0.0.3"
|
||||
dependencies = [
|
||||
"merkle_sigs 0.1.0 (git+https://github.com/SpinResearch/merkle_sigs.rs)",
|
||||
"merkle_sigs 1.0.0 (git+https://github.com/SpinResearch/merkle_sigs.rs)",
|
||||
"protobuf 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.6.0-alpha (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.6.0-alpha1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lamport_sigs"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/SpinResearch/lamport.rs#4ddf030e1514383d13dc86b75c4c239a4935dc48"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/SpinResearch/lamport.rs#9f9fdb749fc62b20404aa4430369e473212c6147"
|
||||
dependencies = [
|
||||
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.6.0-alpha (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.6.0-alpha1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -25,26 +25,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.17"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "merkle"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/SpinResearch/merkle.rs#450325872473884e73790ebd6cf50fa9ce0b8aa8"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/SpinResearch/merkle.rs#249234cacaf2891ee4371846b6a32bfae0743ab9"
|
||||
dependencies = [
|
||||
"protobuf 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.6.0-alpha (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.6.0-alpha1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "merkle_sigs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/SpinResearch/merkle_sigs.rs#ae2e85ab6d694eaed4c5488964af64ffce7bd0f7"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/SpinResearch/merkle_sigs.rs#c9125872e759405e55cdf0609f5c32fece181793"
|
||||
dependencies = [
|
||||
"lamport_sigs 0.1.1 (git+https://github.com/SpinResearch/lamport.rs)",
|
||||
"merkle 0.1.0 (git+https://github.com/SpinResearch/merkle.rs)",
|
||||
"ring 0.6.0-alpha (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lamport_sigs 1.0.0 (git+https://github.com/SpinResearch/lamport.rs)",
|
||||
"merkle 1.0.0 (git+https://github.com/SpinResearch/merkle.rs)",
|
||||
"ring 0.6.0-alpha1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -57,12 +57,12 @@ name = "rand"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.6.0-alpha"
|
||||
version = "0.6.0-alpha1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -71,7 +71,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.21"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -80,13 +80,13 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum lamport_sigs 0.1.1 (git+https://github.com/SpinResearch/lamport.rs)" = "<none>"
|
||||
"checksum lamport_sigs 1.0.0 (git+https://github.com/SpinResearch/lamport.rs)" = "<none>"
|
||||
"checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b"
|
||||
"checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8"
|
||||
"checksum merkle 0.1.0 (git+https://github.com/SpinResearch/merkle.rs)" = "<none>"
|
||||
"checksum merkle_sigs 0.1.0 (git+https://github.com/SpinResearch/merkle_sigs.rs)" = "<none>"
|
||||
"checksum libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a51822fc847e7a8101514d1d44e354ba2ffa7d4c194dcab48870740e327cac70"
|
||||
"checksum merkle 1.0.0 (git+https://github.com/SpinResearch/merkle.rs)" = "<none>"
|
||||
"checksum merkle_sigs 1.0.0 (git+https://github.com/SpinResearch/merkle_sigs.rs)" = "<none>"
|
||||
"checksum protobuf 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "6ec4c2fe04370298218a09ab53a534febf54c160c5554e4de987b6d73c916d5d"
|
||||
"checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d"
|
||||
"checksum ring 0.6.0-alpha (registry+https://github.com/rust-lang/crates.io-index)" = "756e9bcca47cd772b23f9958ea0952a18a5a3e294e4ab3b7d019e6c06955f191"
|
||||
"checksum rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)" = "bff9fc1c79f2dec76b253273d07682e94a978bd8f132ded071188122b2af9818"
|
||||
"checksum ring 0.6.0-alpha1 (registry+https://github.com/rust-lang/crates.io-index)" = "0c9d14fdd6779c80311183b64598d57e640993fd1732119ce2cedb3234217532"
|
||||
"checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b"
|
||||
"checksum untrusted 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "193df64312e3515fd983ded55ad5bcaa7647a035804828ed757e832ce6029ef3"
|
||||
|
@ -1,27 +1,100 @@
|
||||
use std::convert;
|
||||
use std::error;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::num;
|
||||
|
||||
/// Error struct used for generating an `io::Error` from a generic description.
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
pub struct RustyError {
|
||||
descr: &'static str,
|
||||
detail: Option<String>,
|
||||
share_num: Option<u8>,
|
||||
share_groups: Option<Vec<Vec<u8>>>
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub enum RustyErrorTypes {
|
||||
DuplicateShareNum(u8),
|
||||
DuplicateShareData(u8),
|
||||
EmptyShares,
|
||||
IncompatibleSets(Vec<Vec<u8>>),
|
||||
InvalidSignature(u8, String),
|
||||
MissingShares(u8, usize),
|
||||
MissingSignature(u8),
|
||||
ShareParsingError(u8, String)
|
||||
}
|
||||
|
||||
impl RustyError {
|
||||
/// Initializes a new error with a description and optional detail string.
|
||||
pub fn new(descr: &'static str, detail: Option<String>) -> Error {
|
||||
Error {
|
||||
fn new(descr: &'static str, detail: Option<String>, share_num: Option<u8>, share_groups: Option<Vec<Vec<u8>>>) -> RustyError {
|
||||
RustyError {
|
||||
descr: descr,
|
||||
detail: detail,
|
||||
share_num: share_num,
|
||||
share_groups: share_groups
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_type(error_type: RustyErrorTypes) -> RustyError {
|
||||
RustyError {
|
||||
descr: RustyError::descr_for_type(&error_type),
|
||||
detail: RustyError::detail_for_type(&error_type),
|
||||
share_num: RustyError::share_num_for_type(&error_type),
|
||||
share_groups: RustyError::share_groups_for_type(error_type),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn share_num(&self) -> Option<u8> {
|
||||
self.share_num
|
||||
}
|
||||
|
||||
pub fn share_groups(&self) -> Option<Vec<Vec<u8>>> {
|
||||
self.share_groups.clone()
|
||||
}
|
||||
|
||||
fn descr_for_type(error_type: &RustyErrorTypes) -> &'static str {
|
||||
match *error_type {
|
||||
RustyErrorTypes::EmptyShares => "No shares were provided.",
|
||||
RustyErrorTypes::IncompatibleSets(_) => "The shares are incompatible with each other.",
|
||||
RustyErrorTypes::InvalidSignature(_, _) => "The signature of this share is not valid.",
|
||||
RustyErrorTypes::MissingShares(_, _) => "The number of shares provided is insufficient to recover the secret.",
|
||||
RustyErrorTypes::MissingSignature(_) => "Signature is missing while shares are required to be signed.",
|
||||
RustyErrorTypes::ShareParsingError(_, _) => "This share is incorrectly formatted.",
|
||||
RustyErrorTypes::DuplicateShareNum(_) => "This share number has already been used by a previous share.",
|
||||
RustyErrorTypes::DuplicateShareData(_) => "The data encoded in this share is the same as the one found in a previous share."
|
||||
}
|
||||
}
|
||||
|
||||
fn detail_for_type(error_type: &RustyErrorTypes) -> Option<String> {
|
||||
match *error_type {
|
||||
RustyErrorTypes::MissingShares(required, found) => Some(format!("{} shares are required to recover the secret,
|
||||
found only {}.", required, found)),
|
||||
RustyErrorTypes::ShareParsingError(_, ref description) => Some(description.clone()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn share_groups_for_type(error_type: RustyErrorTypes) -> Option<Vec<Vec<u8>>>{
|
||||
match error_type {
|
||||
RustyErrorTypes::IncompatibleSets(groups) => Some(groups),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn share_num_for_type(error_type: &RustyErrorTypes) -> Option<u8> {
|
||||
match *error_type {
|
||||
RustyErrorTypes::InvalidSignature(share_num, _)
|
||||
| RustyErrorTypes::MissingSignature(share_num)
|
||||
| RustyErrorTypes::ShareParsingError(share_num, _)
|
||||
| RustyErrorTypes::DuplicateShareNum(share_num)
|
||||
| RustyErrorTypes::DuplicateShareData(share_num) => Some(share_num),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
impl fmt::Display for RustyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.detail {
|
||||
None => write!(f, "{}", self.descr),
|
||||
@ -30,7 +103,7 @@ impl fmt::Display for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
impl error::Error for RustyError {
|
||||
fn description(&self) -> &str {
|
||||
self.descr
|
||||
}
|
||||
@ -39,33 +112,42 @@ impl error::Error for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for io::Error {
|
||||
fn from(me: Error) -> io::Error {
|
||||
impl From<io::Error> for RustyError {
|
||||
fn from(err: io::Error) -> RustyError {
|
||||
let descr = err.description().to_owned();
|
||||
|
||||
RustyError::new("from io:Error", Some(descr), None, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RustyError> for io::Error {
|
||||
fn from(me: RustyError) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::Other, me)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an `io::Error` from description string and optional detail string.
|
||||
/// Particularly useful in `Result` expressions.
|
||||
pub fn other_io_err(descr: &'static str, detail: Option<String>) -> io::Error {
|
||||
convert::From::from(Error::new(descr, detail))
|
||||
pub fn other_io_err(descr: &'static str, detail: Option<String>,
|
||||
share_num: Option<u8>, share_groups: Option<Vec<Vec<u8>>>) -> io::Error {
|
||||
convert::From::from(RustyError::new(descr, detail, share_num, share_groups))
|
||||
}
|
||||
|
||||
/// maps a `ParseIntError` to an `io::Error`
|
||||
pub fn pie2io(p: num::ParseIntError) -> io::Error {
|
||||
convert::From::from(Error::new("Integer parsing error", Some(p.to_string())))
|
||||
/// maps a `ParseIntError` to an `Error`
|
||||
pub fn pie2error(p: num::ParseIntError) -> RustyError {
|
||||
RustyError::new("Integer parsing error", Some(p.to_string()), None, None)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests_custom_err {
|
||||
use std::error;
|
||||
use custom_error;
|
||||
use custom_error::RustyError;
|
||||
|
||||
#[test]
|
||||
fn test_custom_error() {
|
||||
let desc = "Boring error description";
|
||||
let detail = "More of it";
|
||||
let ewd = custom_error::Error::new(desc, Some(detail.to_string()));
|
||||
let ewd = RustyError::new(desc, Some(detail.to_string()), None, None);
|
||||
|
||||
assert_eq!(error::Error::description(&ewd), desc);
|
||||
match error::Error::cause(&ewd) {
|
||||
@ -73,7 +155,7 @@ mod tests_custom_err {
|
||||
None => assert!(true),
|
||||
}
|
||||
let _formated_err = format!("{}", ewd);
|
||||
let ewod = custom_error::Error::new(desc, None);
|
||||
let ewod = RustyError::new(desc, None, None, None);
|
||||
let _formated_err = format!("{}", ewod);
|
||||
}
|
||||
}
|
||||
@ -81,12 +163,12 @@ mod tests_custom_err {
|
||||
#[cfg(test)]
|
||||
mod tests_std_err {
|
||||
use std::error::Error;
|
||||
use custom_error::pie2io;
|
||||
use custom_error::pie2error;
|
||||
|
||||
#[test]
|
||||
fn test_parse_errors() {
|
||||
let nan = "2a".to_string();
|
||||
match nan.parse::<u8>().map_err(pie2io) {
|
||||
match nan.parse::<u8>().map_err(pie2error) {
|
||||
Ok(_) => assert!(false),
|
||||
Err(x) => assert_eq!("Integer parsing error", x.description()),
|
||||
}
|
||||
|
15
src/lib.rs
15
src/lib.rs
@ -1,10 +1,10 @@
|
||||
#![deny(
|
||||
missing_docs,
|
||||
missing_debug_implementations, missing_copy_implementations,
|
||||
trivial_casts, trivial_numeric_casts,
|
||||
unsafe_code, unstable_features,
|
||||
unused_import_braces, unused_qualifications
|
||||
)]
|
||||
// #![deny(
|
||||
// missing_docs,
|
||||
// missing_debug_implementations, missing_copy_implementations,
|
||||
// trivial_casts, trivial_numeric_casts,
|
||||
// unsafe_code, unstable_features,
|
||||
// unused_import_braces, unused_qualifications
|
||||
// )]
|
||||
|
||||
//! `RustySecrets` implements Shamir Secret Sharing in Rust. It provides the possibility to sign shares.
|
||||
|
||||
@ -28,6 +28,7 @@ mod validation;
|
||||
|
||||
pub use sss::generate_shares;
|
||||
pub use sss::recover_secret;
|
||||
pub use custom_error::RustyError;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
@ -1,22 +1,22 @@
|
||||
use custom_error::{other_io_err, pie2io};
|
||||
use custom_error::{RustyError, RustyErrorTypes};
|
||||
use custom_error::pie2error;
|
||||
use digest;
|
||||
use merkle_sigs::Proof;
|
||||
use merkle_sigs::PublicKey;
|
||||
use merkle_sigs::{MerklePublicKey, Proof, PublicKey};
|
||||
use protobuf;
|
||||
use protobuf::{Message, RepeatedField};
|
||||
use serialize;
|
||||
use serialize::base64::{self, FromBase64, ToBase64};
|
||||
use share_data::ShareData;
|
||||
use std::io;
|
||||
use std::error::Error;
|
||||
|
||||
type ParsedShare = Result<(Vec<u8>, u8, u8, Option<(Vec<Vec<u8>>, Proof<MerklePublicKey>)>), RustyError>;
|
||||
|
||||
fn base64_config() -> serialize::base64::Config {
|
||||
base64::Config { pad: false, ..base64::STANDARD }
|
||||
}
|
||||
|
||||
pub fn share_string_from(share: Vec<u8>,
|
||||
threshold: u8,
|
||||
share_num: u8,
|
||||
signature_pair: Option<(Vec<Vec<u8>>, Proof<PublicKey>)>)
|
||||
pub fn share_string_from(share: Vec<u8>, threshold: u8, share_num: u8,
|
||||
signature_pair: Option<(Vec<Vec<u8>>, Proof<MerklePublicKey>)>)
|
||||
-> String {
|
||||
let mut share_protobuf = ShareData::new();
|
||||
share_protobuf.set_shamir_data(share);
|
||||
@ -33,44 +33,45 @@ pub fn share_string_from(share: Vec<u8>,
|
||||
|
||||
pub fn share_from_string
|
||||
(s: &str,
|
||||
index: u8,
|
||||
is_signed: bool)
|
||||
-> io::Result<(Vec<u8>, u8, u8, Option<(Vec<Vec<u8>>, Proof<PublicKey>)>)> {
|
||||
-> ParsedShare {
|
||||
let parts: Vec<_> = s.trim().split('-').collect();
|
||||
|
||||
if parts.len() != 3 {
|
||||
return Err(other_io_err("Share parse error: Expected 3 parts separated by a minus sign",
|
||||
None));
|
||||
return Err(RustyError::with_type(RustyErrorTypes::ShareParsingError(index, format!("Expected 3 parts separated by a minus sign. Found {}.", s))));
|
||||
}
|
||||
let (k, n, p3) = {
|
||||
let mut iter = parts.into_iter();
|
||||
let k = try!(iter.next().unwrap().parse::<u8>().map_err(pie2io));
|
||||
let n = try!(iter.next().unwrap().parse::<u8>().map_err(pie2io));
|
||||
let k = try!(iter.next().unwrap().parse::<u8>().map_err(pie2error));
|
||||
let n = try!(iter.next().unwrap().parse::<u8>().map_err(pie2error));
|
||||
let p3 = iter.next().unwrap();
|
||||
(k, n, p3)
|
||||
};
|
||||
if k < 1 || n < 1 {
|
||||
return Err(other_io_err("Share parse error: Illegal K,N parameters", None));
|
||||
return Err(RustyError::with_type(RustyErrorTypes::ShareParsingError(index, format!("Found illegal parameters K: {} N: {}.", k, n))));
|
||||
}
|
||||
|
||||
let raw_data = try!(p3.from_base64().map_err(|_| {
|
||||
other_io_err("Share parse error: Base64 decoding of data block failed",
|
||||
None)
|
||||
RustyError::with_type(RustyErrorTypes::ShareParsingError(index, "Base64 decoding of data block failed".to_owned()))
|
||||
}));
|
||||
|
||||
let protobuf_data = try!(protobuf::parse_from_bytes::<ShareData>(raw_data.as_slice())
|
||||
.map_err(|_| other_io_err("Share parse error: Protobuffer could not be decoded.", None)));
|
||||
|
||||
.map_err(|e| RustyError::with_type(RustyErrorTypes::ShareParsingError(index, format!("Protobuf decoding of data block failed with error: {} .", e.description())))));
|
||||
|
||||
let share = Vec::from(protobuf_data.get_shamir_data());
|
||||
|
||||
if is_signed {
|
||||
let p = Proof::parse_from_bytes(protobuf_data.get_proof(), digest).unwrap().unwrap();
|
||||
let p_result = Proof::parse_from_bytes(protobuf_data.get_proof(), digest);
|
||||
|
||||
let p_opt = p_result.unwrap();
|
||||
let p = p_opt.unwrap();
|
||||
|
||||
let proof = Proof {
|
||||
algorithm: digest,
|
||||
lemma: p.lemma,
|
||||
root_hash: p.root_hash,
|
||||
value: PublicKey::from_vec(p.value, digest).unwrap(),
|
||||
value: MerklePublicKey::new(PublicKey::from_vec(p.value, digest).unwrap()),
|
||||
};
|
||||
|
||||
let signature = protobuf_data.get_signature();
|
||||
|
@ -1,4 +1,4 @@
|
||||
use custom_error::other_io_err;
|
||||
use custom_error::{RustyError, other_io_err};
|
||||
use digest;
|
||||
use interpolation::{encode, lagrange_interpolate};
|
||||
use merkle_sigs::sign_data_vec;
|
||||
@ -31,7 +31,7 @@ fn new_vec<T: Clone>(n: usize, x: T) -> Vec<T> {
|
||||
/// ```
|
||||
pub fn generate_shares(k: u8, n: u8, secret: &[u8], sign_shares: bool) -> io::Result<Vec<String>> {
|
||||
if k > n {
|
||||
return Err(other_io_err("Threshold K can not be larger than N", None));
|
||||
return Err(other_io_err("Threshold K can not be larger than N", None, None, None));
|
||||
}
|
||||
|
||||
let shares = try!(secret_share(&*secret, k, n));
|
||||
@ -66,7 +66,7 @@ pub fn generate_shares(k: u8, n: u8, secret: &[u8], sign_shares: bool) -> io::Re
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn secret_share(src: &[u8], k: u8, n: u8) -> io::Result<Vec<Vec<u8>>> {
|
||||
pub fn secret_share(src: &[u8], k: u8, n: u8) -> Result<Vec<Vec<u8>>, RustyError> {
|
||||
let mut result = Vec::with_capacity(n as usize);
|
||||
for _ in 0..(n as usize) {
|
||||
result.push(new_vec(src.len(), 0u8));
|
||||
@ -108,7 +108,7 @@ pub fn secret_share(src: &[u8], k: u8, n: u8) -> io::Result<Vec<Vec<u8>>> {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn recover_secret(shares: Vec<String>, verify_signatures: bool) -> io::Result<Vec<u8>> {
|
||||
pub fn recover_secret(shares: Vec<String>, verify_signatures: bool) -> Result<Vec<u8>, RustyError> {
|
||||
let (k, shares) = try!(process_and_validate_shares(shares, verify_signatures));
|
||||
|
||||
let slen = shares[0].1.len();
|
||||
|
@ -1,69 +1,109 @@
|
||||
use custom_error::other_io_err;
|
||||
use custom_error::{RustyError, RustyErrorTypes};
|
||||
use merkle_sigs::verify_data_vec_signature;
|
||||
use share_format;
|
||||
use share_format::format_share_for_signing;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
|
||||
type ProcessedShares = Result<(u8, Vec<(u8, Vec<u8>)>), RustyError>;
|
||||
|
||||
// The order of validation that we think makes the most sense is the following:
|
||||
// 1) Validate shares individually
|
||||
// 2) Validate duplicate shares share num && data
|
||||
// 2) Validate group consistency
|
||||
// 3) Validate other properties, in no specific order
|
||||
|
||||
pub fn process_and_validate_shares(shares_strings: Vec<String>,
|
||||
verify_signatures: bool)
|
||||
-> io::Result<(u8, Vec<(u8, Vec<u8>)>)> {
|
||||
let mut opt_k: Option<u8> = None;
|
||||
let mut opt_root_hash: Option<Vec<u8>> = None;
|
||||
|
||||
-> ProcessedShares {
|
||||
let mut shares: Vec<(u8, Vec<u8>)> = Vec::new();
|
||||
|
||||
for (counter, line) in shares_strings.iter().enumerate() {
|
||||
let (share_data, k, n, sig_pair) = try!(share_format::share_from_string(line,
|
||||
verify_signatures));
|
||||
let mut k_compatibility_sets = HashMap::new();
|
||||
let mut rh_compatibility_sets = HashMap::new();
|
||||
|
||||
for (counter, line) in shares_strings.iter().enumerate() {
|
||||
if k_compatibility_sets.len() == 1 {
|
||||
let k = k_compatibility_sets.keys().last().unwrap();
|
||||
if *k == shares.len() as u8 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let share_index = counter as u8;
|
||||
let (share_data, k, n, sig_pair) = try!(share_format::share_from_string(line,
|
||||
counter as u8,
|
||||
verify_signatures));
|
||||
if verify_signatures {
|
||||
if sig_pair.is_none() {
|
||||
return Err(other_io_err("Signature is missing while shares are required to be \
|
||||
signed.",
|
||||
None));
|
||||
return Err(RustyError::with_type(RustyErrorTypes::MissingSignature(share_index)));
|
||||
}
|
||||
|
||||
let (signature, p) = sig_pair.unwrap();
|
||||
let root_hash = p.root_hash.clone();
|
||||
|
||||
if let Some(rh) = opt_root_hash.clone() {
|
||||
if root_hash != rh {
|
||||
return Err(other_io_err("Root hash not matching", None));
|
||||
}
|
||||
p.validate(&rh);
|
||||
} else {
|
||||
opt_root_hash = Some(root_hash.clone());
|
||||
}
|
||||
|
||||
try!(verify_data_vec_signature(format_share_for_signing(k,
|
||||
n,
|
||||
&share_data.as_slice()),
|
||||
&(signature.to_vec(), p),
|
||||
&root_hash)
|
||||
.map_err(|e| other_io_err("Invalid signature", Some(String::from(e.description())))));
|
||||
.map_err(|e| RustyError::with_type(RustyErrorTypes::InvalidSignature(share_index, String::from(e.description())))));
|
||||
rh_compatibility_sets.entry(root_hash.clone()).or_insert_with(Vec::new);
|
||||
let vec = rh_compatibility_sets.get_mut(&root_hash).unwrap();
|
||||
vec.push(share_index);
|
||||
}
|
||||
|
||||
if let Some(k_global) = opt_k {
|
||||
if k != k_global {
|
||||
return Err(other_io_err("Incompatible shares", None));
|
||||
}
|
||||
} else {
|
||||
opt_k = Some(k);
|
||||
}
|
||||
k_compatibility_sets.entry(k).or_insert_with(Vec::new);
|
||||
let vec = k_compatibility_sets.get_mut(&k).unwrap();
|
||||
vec.push(share_index);
|
||||
|
||||
if shares.iter().any(|s| s.0 == n) {
|
||||
return Err(other_io_err("Duplicate Share Number", None));
|
||||
return Err(RustyError::with_type(RustyErrorTypes::DuplicateShareNum(share_index)));
|
||||
};
|
||||
|
||||
if shares.iter().any(|s| s.1 == share_data) {
|
||||
return Err(other_io_err("Duplicate Share Data", None));
|
||||
return Err(RustyError::with_type(RustyErrorTypes::DuplicateShareData(share_index)));
|
||||
};
|
||||
|
||||
shares.push((n, share_data));
|
||||
if counter + 1 == k as usize {
|
||||
return Ok((k, shares));
|
||||
}
|
||||
|
||||
// Validate k
|
||||
|
||||
let k_sets = k_compatibility_sets.keys().count();
|
||||
let rh_sets = rh_compatibility_sets.keys().count();
|
||||
|
||||
if verify_signatures {
|
||||
match rh_sets {
|
||||
0 => {
|
||||
return Err(RustyError::with_type(RustyErrorTypes::EmptyShares))
|
||||
}
|
||||
1 => { } // All shares have the same roothash.
|
||||
_ => {
|
||||
return Err(RustyError::with_type(RustyErrorTypes::IncompatibleSets(rh_compatibility_sets.values()
|
||||
.map(|x| x.to_owned()).collect())))
|
||||
}
|
||||
}
|
||||
Err(other_io_err("Not enough shares provided!", None))
|
||||
}
|
||||
|
||||
match k_sets {
|
||||
0 => {
|
||||
return Err(RustyError::with_type(RustyErrorTypes::EmptyShares))
|
||||
}
|
||||
1 => { } // All shares have the same roothash.
|
||||
_ => {
|
||||
return Err(RustyError::with_type(RustyErrorTypes::IncompatibleSets(k_compatibility_sets.values()
|
||||
.map(|x| x.to_owned()).collect())))
|
||||
}
|
||||
}
|
||||
|
||||
// It is safe to unwrap because k_sets == 1
|
||||
let k = *k_compatibility_sets.keys().last().unwrap();
|
||||
let shares_num = shares.len();
|
||||
|
||||
if shares_num >= k as usize {
|
||||
shares.truncate(k as usize);
|
||||
Ok((k, shares))
|
||||
} else {
|
||||
Err(RustyError::with_type(RustyErrorTypes::MissingShares(k, shares_num)))
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,12 @@ fn test_reasonable_splits() {
|
||||
across public lines."
|
||||
.to_string()
|
||||
.into_bytes();
|
||||
for is_signing in vec![true, false] {
|
||||
for k in 2..max_shares {
|
||||
for is_signing in &[true, false] {
|
||||
for k in 1..max_shares {
|
||||
for n in k..max_shares {
|
||||
let shares = generate_shares(k, n, &secret, is_signing).unwrap();
|
||||
let shares = generate_shares(k, n, &secret, *is_signing).unwrap();
|
||||
println!("Testing {} out-of- {}", k, n);
|
||||
assert_eq!(secret, recover_secret(shares, is_signing).unwrap());
|
||||
assert_eq!(secret, recover_secret(shares, *is_signing).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,21 @@ extern crate rusty_secrets;
|
||||
use rusty_secrets::recover_secret;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Not enough shares provided!")]
|
||||
fn test_recover_sellibitze_no_shares() {
|
||||
#[should_panic(expected = "No shares were provided.")]
|
||||
fn test_recover_no_shares() {
|
||||
let shares = vec![];
|
||||
recover_secret(shares, false).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Share parse error: Expected 3 parts separated by a minus sign")]
|
||||
#[should_panic(expected = "No shares were provided.")]
|
||||
fn test_recover_no_shares_signed() {
|
||||
let shares = vec![];
|
||||
recover_secret(shares, true).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "This share is incorrectly formatted.")]
|
||||
fn test_recover_2_parts_share() {
|
||||
let share1 = "2-1-CgmKQZHMO+5n5pU".to_string();
|
||||
let share2 = "2-2".to_string();
|
||||
@ -32,7 +39,7 @@ fn test_recover_incorrect_share_num() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Share parse error: Illegal K,N parameters")]
|
||||
#[should_panic(expected = "This share is incorrectly formatted.")]
|
||||
fn test_recover_0_share_num() {
|
||||
let share1 = "2-0-1YAYwmOHqZ69jA".to_string();
|
||||
let share2 = "2-1-YJZQDGm22Y77Gw".to_string();
|
||||
@ -43,7 +50,7 @@ fn test_recover_0_share_num() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Share parse error: Base64 decoding of data block failed")]
|
||||
#[should_panic(expected = "This share is incorrectly formatted.")]
|
||||
fn test_recover_invalid_b64() {
|
||||
let share1 = "2-1-CgnlCxRNtnkzENE".to_string();
|
||||
let share2 = "2-1-YJZQDG((((m22Y)))77Gw".to_string();
|
||||
@ -54,7 +61,7 @@ fn test_recover_invalid_b64() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Duplicate Share Number")]
|
||||
#[should_panic(expected = "This share number has already been used by a previous share.")]
|
||||
fn test_recover_duplicate_shares_number() {
|
||||
let share1 = "2-1-CgnlCxRNtnkzENE".to_string();
|
||||
let share2 = "2-1-CgkAnUgP3lfwjyM".to_string();
|
||||
@ -65,7 +72,7 @@ fn test_recover_duplicate_shares_number() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Duplicate Share Data")]
|
||||
#[should_panic(expected = "The data encoded in this share is the same as the one found in a previous share.")]
|
||||
fn test_recover_duplicate_shares_data() {
|
||||
let share1 = "2-1-CgnlCxRNtnkzENE".to_string();
|
||||
let share2 = "2-2-CgnlCxRNtnkzENE".to_string();
|
||||
@ -76,7 +83,7 @@ fn test_recover_duplicate_shares_data() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Not enough shares provided!")]
|
||||
#[should_panic(expected = "The number of shares provided is insufficient to recover the secret.")]
|
||||
fn test_recover_too_few_shares() {
|
||||
let share1 = "3-1-ChbcCdSZOaMn6DM1jFca2P6/0WRlP7AK".to_string();
|
||||
let share2 = "3-2-ChbG46L1zRszs0PPn63XnnupmZTcgYJ3".to_string();
|
||||
|
Reference in New Issue
Block a user