Adding share_num field to errors.

This commit is contained in:
Frederic Jacobs
2016-12-20 23:10:47 +01:00
committed by GitHub
parent f5ab309dd6
commit ef4d525703
8 changed files with 250 additions and 119 deletions

48
Cargo.lock generated
View File

@ -2,20 +2,20 @@
name = "rusty_secrets" name = "rusty_secrets"
version = "0.0.3" version = "0.0.3"
dependencies = [ 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)", "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)", "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)",
"rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "lamport_sigs" name = "lamport_sigs"
version = "0.1.1" version = "1.0.0"
source = "git+https://github.com/SpinResearch/lamport.rs#4ddf030e1514383d13dc86b75c4c239a4935dc48" source = "git+https://github.com/SpinResearch/lamport.rs#9f9fdb749fc62b20404aa4430369e473212c6147"
dependencies = [ dependencies = [
"rand 0.3.15 (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)", "ring 0.6.0-alpha1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -25,26 +25,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.17" version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "merkle" name = "merkle"
version = "0.1.0" version = "1.0.0"
source = "git+https://github.com/SpinResearch/merkle.rs#450325872473884e73790ebd6cf50fa9ce0b8aa8" source = "git+https://github.com/SpinResearch/merkle.rs#249234cacaf2891ee4371846b6a32bfae0743ab9"
dependencies = [ dependencies = [
"protobuf 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "merkle_sigs" name = "merkle_sigs"
version = "0.1.0" version = "1.0.0"
source = "git+https://github.com/SpinResearch/merkle_sigs.rs#ae2e85ab6d694eaed4c5488964af64ffce7bd0f7" source = "git+https://github.com/SpinResearch/merkle_sigs.rs#c9125872e759405e55cdf0609f5c32fece181793"
dependencies = [ dependencies = [
"lamport_sigs 0.1.1 (git+https://github.com/SpinResearch/lamport.rs)", "lamport_sigs 1.0.0 (git+https://github.com/SpinResearch/lamport.rs)",
"merkle 0.1.0 (git+https://github.com/SpinResearch/merkle.rs)", "merkle 1.0.0 (git+https://github.com/SpinResearch/merkle.rs)",
"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]] [[package]]
@ -57,12 +57,12 @@ name = "rand"
version = "0.3.15" version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ 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]] [[package]]
name = "ring" name = "ring"
version = "0.6.0-alpha" version = "0.6.0-alpha1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -71,7 +71,7 @@ dependencies = [
[[package]] [[package]]
name = "rustc-serialize" name = "rustc-serialize"
version = "0.3.21" version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -80,13 +80,13 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [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 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 libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a51822fc847e7a8101514d1d44e354ba2ffa7d4c194dcab48870740e327cac70"
"checksum merkle 0.1.0 (git+https://github.com/SpinResearch/merkle.rs)" = "<none>" "checksum merkle 1.0.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 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 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 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 ring 0.6.0-alpha1 (registry+https://github.com/rust-lang/crates.io-index)" = "0c9d14fdd6779c80311183b64598d57e640993fd1732119ce2cedb3234217532"
"checksum rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)" = "bff9fc1c79f2dec76b253273d07682e94a978bd8f132ded071188122b2af9818" "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" "checksum untrusted 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "193df64312e3515fd983ded55ad5bcaa7647a035804828ed757e832ce6029ef3"

View File

@ -1,27 +1,100 @@
use std::convert; use std::convert;
use std::error; use std::error;
use std::error::Error;
use std::fmt; use std::fmt;
use std::io; use std::io;
use std::num; use std::num;
/// Error struct used for generating an `io::Error` from a generic description. /// Error struct used for generating an `io::Error` from a generic description.
#[derive(Debug)] #[derive(Debug)]
pub struct Error { pub struct RustyError {
descr: &'static str, descr: &'static str,
detail: Option<String>, 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. /// Initializes a new error with a description and optional detail string.
pub fn new(descr: &'static str, detail: Option<String>) -> Error { fn new(descr: &'static str, detail: Option<String>, share_num: Option<u8>, share_groups: Option<Vec<Vec<u8>>>) -> RustyError {
Error { RustyError {
descr: descr, descr: descr,
detail: detail, 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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.detail { match self.detail {
None => write!(f, "{}", self.descr), 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 { fn description(&self) -> &str {
self.descr self.descr
} }
@ -39,33 +112,42 @@ impl error::Error for Error {
} }
} }
impl From<Error> for io::Error { impl From<io::Error> for RustyError {
fn from(me: Error) -> io::Error { 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) io::Error::new(io::ErrorKind::Other, me)
} }
} }
/// Returns an `io::Error` from description string and optional detail string. /// Returns an `io::Error` from description string and optional detail string.
/// Particularly useful in `Result` expressions. /// Particularly useful in `Result` expressions.
pub fn other_io_err(descr: &'static str, detail: Option<String>) -> io::Error { pub fn other_io_err(descr: &'static str, detail: Option<String>,
convert::From::from(Error::new(descr, detail)) 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` /// maps a `ParseIntError` to an `Error`
pub fn pie2io(p: num::ParseIntError) -> io::Error { pub fn pie2error(p: num::ParseIntError) -> RustyError {
convert::From::from(Error::new("Integer parsing error", Some(p.to_string()))) RustyError::new("Integer parsing error", Some(p.to_string()), None, None)
} }
#[cfg(test)] #[cfg(test)]
mod tests_custom_err { mod tests_custom_err {
use std::error; use std::error;
use custom_error; use custom_error::RustyError;
#[test] #[test]
fn test_custom_error() { fn test_custom_error() {
let desc = "Boring error description"; let desc = "Boring error description";
let detail = "More of it"; 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); assert_eq!(error::Error::description(&ewd), desc);
match error::Error::cause(&ewd) { match error::Error::cause(&ewd) {
@ -73,7 +155,7 @@ mod tests_custom_err {
None => assert!(true), None => assert!(true),
} }
let _formated_err = format!("{}", ewd); 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); let _formated_err = format!("{}", ewod);
} }
} }
@ -81,12 +163,12 @@ mod tests_custom_err {
#[cfg(test)] #[cfg(test)]
mod tests_std_err { mod tests_std_err {
use std::error::Error; use std::error::Error;
use custom_error::pie2io; use custom_error::pie2error;
#[test] #[test]
fn test_parse_errors() { fn test_parse_errors() {
let nan = "2a".to_string(); let nan = "2a".to_string();
match nan.parse::<u8>().map_err(pie2io) { match nan.parse::<u8>().map_err(pie2error) {
Ok(_) => assert!(false), Ok(_) => assert!(false),
Err(x) => assert_eq!("Integer parsing error", x.description()), Err(x) => assert_eq!("Integer parsing error", x.description()),
} }

View File

@ -1,10 +1,10 @@
#![deny( // #![deny(
missing_docs, // missing_docs,
missing_debug_implementations, missing_copy_implementations, // missing_debug_implementations, missing_copy_implementations,
trivial_casts, trivial_numeric_casts, // trivial_casts, trivial_numeric_casts,
unsafe_code, unstable_features, // unsafe_code, unstable_features,
unused_import_braces, unused_qualifications // unused_import_braces, unused_qualifications
)] // )]
//! `RustySecrets` implements Shamir Secret Sharing in Rust. It provides the possibility to sign shares. //! `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::generate_shares;
pub use sss::recover_secret; pub use sss::recover_secret;
pub use custom_error::RustyError;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View File

@ -1,22 +1,22 @@
use custom_error::{other_io_err, pie2io}; use custom_error::{RustyError, RustyErrorTypes};
use custom_error::pie2error;
use digest; use digest;
use merkle_sigs::Proof; use merkle_sigs::{MerklePublicKey, Proof, PublicKey};
use merkle_sigs::PublicKey;
use protobuf; use protobuf;
use protobuf::{Message, RepeatedField}; use protobuf::{Message, RepeatedField};
use serialize; use serialize;
use serialize::base64::{self, FromBase64, ToBase64}; use serialize::base64::{self, FromBase64, ToBase64};
use share_data::ShareData; 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 { fn base64_config() -> serialize::base64::Config {
base64::Config { pad: false, ..base64::STANDARD } base64::Config { pad: false, ..base64::STANDARD }
} }
pub fn share_string_from(share: Vec<u8>, pub fn share_string_from(share: Vec<u8>, threshold: u8, share_num: u8,
threshold: u8, signature_pair: Option<(Vec<Vec<u8>>, Proof<MerklePublicKey>)>)
share_num: u8,
signature_pair: Option<(Vec<Vec<u8>>, Proof<PublicKey>)>)
-> String { -> String {
let mut share_protobuf = ShareData::new(); let mut share_protobuf = ShareData::new();
share_protobuf.set_shamir_data(share); share_protobuf.set_shamir_data(share);
@ -33,44 +33,45 @@ pub fn share_string_from(share: Vec<u8>,
pub fn share_from_string pub fn share_from_string
(s: &str, (s: &str,
index: u8,
is_signed: bool) is_signed: bool)
-> io::Result<(Vec<u8>, u8, u8, Option<(Vec<Vec<u8>>, Proof<PublicKey>)>)> { -> ParsedShare {
let parts: Vec<_> = s.trim().split('-').collect(); let parts: Vec<_> = s.trim().split('-').collect();
if parts.len() != 3 { if parts.len() != 3 {
return Err(other_io_err("Share parse error: Expected 3 parts separated by a minus sign", return Err(RustyError::with_type(RustyErrorTypes::ShareParsingError(index, format!("Expected 3 parts separated by a minus sign. Found {}.", s))));
None));
} }
let (k, n, p3) = { let (k, n, p3) = {
let mut iter = parts.into_iter(); let mut iter = parts.into_iter();
let k = 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(pie2io)); let n = try!(iter.next().unwrap().parse::<u8>().map_err(pie2error));
let p3 = iter.next().unwrap(); let p3 = iter.next().unwrap();
(k, n, p3) (k, n, p3)
}; };
if k < 1 || n < 1 { 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(|_| { let raw_data = try!(p3.from_base64().map_err(|_| {
other_io_err("Share parse error: Base64 decoding of data block failed", RustyError::with_type(RustyErrorTypes::ShareParsingError(index, "Base64 decoding of data block failed".to_owned()))
None)
})); }));
let protobuf_data = try!(protobuf::parse_from_bytes::<ShareData>(raw_data.as_slice()) 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()); let share = Vec::from(protobuf_data.get_shamir_data());
if is_signed { 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 { let proof = Proof {
algorithm: digest, algorithm: digest,
lemma: p.lemma, lemma: p.lemma,
root_hash: p.root_hash, 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(); let signature = protobuf_data.get_signature();

View File

@ -1,4 +1,4 @@
use custom_error::other_io_err; use custom_error::{RustyError, other_io_err};
use digest; use digest;
use interpolation::{encode, lagrange_interpolate}; use interpolation::{encode, lagrange_interpolate};
use merkle_sigs::sign_data_vec; 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>> { pub fn generate_shares(k: u8, n: u8, secret: &[u8], sign_shares: bool) -> io::Result<Vec<String>> {
if k > n { 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)); 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) 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); let mut result = Vec::with_capacity(n as usize);
for _ in 0..(n as usize) { for _ in 0..(n as usize) {
result.push(new_vec(src.len(), 0u8)); 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 (k, shares) = try!(process_and_validate_shares(shares, verify_signatures));
let slen = shares[0].1.len(); let slen = shares[0].1.len();

View File

@ -1,69 +1,109 @@
use custom_error::other_io_err; use custom_error::{RustyError, RustyErrorTypes};
use merkle_sigs::verify_data_vec_signature; use merkle_sigs::verify_data_vec_signature;
use share_format; use share_format;
use share_format::format_share_for_signing; use share_format::format_share_for_signing;
use std::collections::HashMap;
use std::error::Error; 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>, pub fn process_and_validate_shares(shares_strings: Vec<String>,
verify_signatures: bool) verify_signatures: bool)
-> io::Result<(u8, Vec<(u8, Vec<u8>)>)> { -> ProcessedShares {
let mut opt_k: Option<u8> = None;
let mut opt_root_hash: Option<Vec<u8>> = None;
let mut shares: Vec<(u8, Vec<u8>)> = Vec::new(); let mut shares: Vec<(u8, Vec<u8>)> = Vec::new();
for (counter, line) in shares_strings.iter().enumerate() { let mut k_compatibility_sets = HashMap::new();
let (share_data, k, n, sig_pair) = try!(share_format::share_from_string(line, let mut rh_compatibility_sets = HashMap::new();
verify_signatures));
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 verify_signatures {
if sig_pair.is_none() { if sig_pair.is_none() {
return Err(other_io_err("Signature is missing while shares are required to be \ return Err(RustyError::with_type(RustyErrorTypes::MissingSignature(share_index)));
signed.",
None));
} }
let (signature, p) = sig_pair.unwrap(); let (signature, p) = sig_pair.unwrap();
let root_hash = p.root_hash.clone(); 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, try!(verify_data_vec_signature(format_share_for_signing(k,
n, n,
&share_data.as_slice()), &share_data.as_slice()),
&(signature.to_vec(), p), &(signature.to_vec(), p),
&root_hash) &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 { k_compatibility_sets.entry(k).or_insert_with(Vec::new);
if k != k_global { let vec = k_compatibility_sets.get_mut(&k).unwrap();
return Err(other_io_err("Incompatible shares", None)); vec.push(share_index);
}
} else {
opt_k = Some(k);
}
if shares.iter().any(|s| s.0 == n) { 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) { 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)); 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)))
}
} }

View File

@ -13,12 +13,12 @@ fn test_reasonable_splits() {
across public lines." across public lines."
.to_string() .to_string()
.into_bytes(); .into_bytes();
for is_signing in vec![true, false] { for is_signing in &[true, false] {
for k in 2..max_shares { for k in 1..max_shares {
for n in k..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); println!("Testing {} out-of- {}", k, n);
assert_eq!(secret, recover_secret(shares, is_signing).unwrap()); assert_eq!(secret, recover_secret(shares, *is_signing).unwrap());
} }
} }
} }

View File

@ -3,14 +3,21 @@ extern crate rusty_secrets;
use rusty_secrets::recover_secret; use rusty_secrets::recover_secret;
#[test] #[test]
#[should_panic(expected = "Not enough shares provided!")] #[should_panic(expected = "No shares were provided.")]
fn test_recover_sellibitze_no_shares() { fn test_recover_no_shares() {
let shares = vec![]; let shares = vec![];
recover_secret(shares, false).unwrap(); recover_secret(shares, false).unwrap();
} }
#[test] #[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() { fn test_recover_2_parts_share() {
let share1 = "2-1-CgmKQZHMO+5n5pU".to_string(); let share1 = "2-1-CgmKQZHMO+5n5pU".to_string();
let share2 = "2-2".to_string(); let share2 = "2-2".to_string();
@ -32,7 +39,7 @@ fn test_recover_incorrect_share_num() {
} }
#[test] #[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() { fn test_recover_0_share_num() {
let share1 = "2-0-1YAYwmOHqZ69jA".to_string(); let share1 = "2-0-1YAYwmOHqZ69jA".to_string();
let share2 = "2-1-YJZQDGm22Y77Gw".to_string(); let share2 = "2-1-YJZQDGm22Y77Gw".to_string();
@ -43,7 +50,7 @@ fn test_recover_0_share_num() {
} }
#[test] #[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() { fn test_recover_invalid_b64() {
let share1 = "2-1-CgnlCxRNtnkzENE".to_string(); let share1 = "2-1-CgnlCxRNtnkzENE".to_string();
let share2 = "2-1-YJZQDG((((m22Y)))77Gw".to_string(); let share2 = "2-1-YJZQDG((((m22Y)))77Gw".to_string();
@ -54,7 +61,7 @@ fn test_recover_invalid_b64() {
} }
#[test] #[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() { fn test_recover_duplicate_shares_number() {
let share1 = "2-1-CgnlCxRNtnkzENE".to_string(); let share1 = "2-1-CgnlCxRNtnkzENE".to_string();
let share2 = "2-1-CgkAnUgP3lfwjyM".to_string(); let share2 = "2-1-CgkAnUgP3lfwjyM".to_string();
@ -65,7 +72,7 @@ fn test_recover_duplicate_shares_number() {
} }
#[test] #[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() { fn test_recover_duplicate_shares_data() {
let share1 = "2-1-CgnlCxRNtnkzENE".to_string(); let share1 = "2-1-CgnlCxRNtnkzENE".to_string();
let share2 = "2-2-CgnlCxRNtnkzENE".to_string(); let share2 = "2-2-CgnlCxRNtnkzENE".to_string();
@ -76,7 +83,7 @@ fn test_recover_duplicate_shares_data() {
} }
#[test] #[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() { fn test_recover_too_few_shares() {
let share1 = "3-1-ChbcCdSZOaMn6DM1jFca2P6/0WRlP7AK".to_string(); let share1 = "3-1-ChbcCdSZOaMn6DM1jFca2P6/0WRlP7AK".to_string();
let share2 = "3-2-ChbG46L1zRszs0PPn63XnnupmZTcgYJ3".to_string(); let share2 = "3-2-ChbG46L1zRszs0PPn63XnnupmZTcgYJ3".to_string();