diff --git a/.travis.yml b/.travis.yml index 497f53c..4e22e71 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,18 @@ +addons: + apt: + packages: + - libcurl4-openssl-dev + - libelf-dev + - libdw-dev language: rust + +before_script: + - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH + script: - - cargo build --verbose -notifications: - email: - on_success: never + - | + travis-cargo build && + travis-cargo test +after_success: + - travis-cargo coveralls --no-sudo + diff --git a/Cargo.toml b/Cargo.toml index 31c51f4..6ef96ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "rusty_secrets" version = "0.0.2" description = "Implementation of threshold Shamir secret sharing in the Rust programming language." +homepage = "https://github.com/freedomofpress/RustySecrets" license = "GPLv3" readme = "README.md" build = "build.rs" diff --git a/README.md b/README.md index 2a33f3f..6048079 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Rusty Secrets +# Rusty Secrets ![Travis-Badge](https://travis-ci.org/freedomofpress/RustySecrets.svg) [![Coverage Status](https://coveralls.io/repos/github/freedomofpress/RustySecrets/badge.svg?branch=master)](https://coveralls.io/github/freedomofpress/RustySecrets?branch=master) Rusty Secrets is an implementation of a threshold [Shamir's secret sharing scheme](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing). diff --git a/src/lib/custom_error.rs b/src/lib/custom_error.rs index 9d15c83..6bf6393 100644 --- a/src/lib/custom_error.rs +++ b/src/lib/custom_error.rs @@ -50,3 +50,19 @@ pub fn pie2io(p: num::ParseIntError) -> io::Error { Error::new("Integer parsing error", Some(p.to_string())) ) } + +#[test] +fn test_custom_error() { + let desc = "Boring error description"; + let detail = "More of it"; + let ewd = Error::new(desc, Some(detail.to_string())); + + assert_eq!(error::Error::description(&ewd), desc); + match error::Error::cause(&ewd) { + Some(_) => assert!(false), + None => assert!(true), + } + let _formated_err = format!("{}", ewd); + let ewod = Error::new(desc, None); + let _formated_err = format!("{}", ewod); +} diff --git a/src/lib/mod.rs b/src/lib/mod.rs index 663ba04..f627a60 100644 --- a/src/lib/mod.rs +++ b/src/lib/mod.rs @@ -15,6 +15,10 @@ pub mod custom_error; use self::custom_error::*; pub fn generate_shares(k: u8, n: u8, secret: &Vec) -> io::Result> { + if k > n { + return Err(other_io_err("Threshold K can not be larger than N", None)); + } + let shares = try!(secret_share(&*secret, k, n)); let config = base64::Config { pad: false, @@ -40,8 +44,7 @@ pub fn process_shares(shares_strings: Vec) -> io::Result<(u8, Vec<(u8,Ve for line in shares_strings { let parts: Vec<_> = line.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(other_io_err("Share parse error: Expected 3 parts separated by a minus sign", None)); } let (k, n, p3) = { let mut iter = parts.into_iter(); @@ -64,20 +67,25 @@ pub fn process_shares(shares_strings: Vec) -> io::Result<(u8, Vec<(u8,Ve } else { opt_k_l = Some((k, data.len())); } - if shares.iter().all(|s| s.0 != n) { - shares.push((n, data)); - counter += 1; - if counter == k { - return Ok((k, shares)); - } + + if shares.iter().any(|s| s.0 == n) { + return Err(other_io_err("Duplicate Share Number", None)); + }; + + if shares.iter().any(|s| s.1 == data) { + return Err(other_io_err("Duplicate Share Data", None)); + }; + + shares.push((n, data)); + counter += 1; + if counter == k { + return Ok((k, shares)); } } Err(other_io_err("Not enough shares provided!", None)) } pub fn recover_secret(shares_strings: Vec) -> io::Result> { - assert!(!shares_strings.is_empty()); - let (k, shares) = try!(process_shares(shares_strings)); let slen = shares[0].1.len(); diff --git a/src/main.rs b/src/main.rs index 8752e80..d547147 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ extern crate getopts; use getopts::Options; -use std::str; use lib::custom_error::*; mod lib; diff --git a/tests/generation-errors.rs b/tests/generation-errors.rs new file mode 100644 index 0000000..e4f36aa --- /dev/null +++ b/tests/generation-errors.rs @@ -0,0 +1,22 @@ +extern crate rusty_secrets; + +use rusty_secrets::custom_error::{pie2io}; +use rusty_secrets::generate_shares; +use std::error::Error; + +#[test] +#[should_panic(expected = "Threshold K can not be larger than N")] +fn test_generate_invalid_k() { + let share1 = "2-1-1YAYwmOHqZ69jA".to_string().into_bytes(); + + generate_shares(10, 5, &share1).unwrap(); +} + +#[test] +fn test_parse_errors() { + let nan = "2a".to_string(); + match nan.parse::().map_err(pie2io) { + Ok(_) => assert!(false), + Err(x) => assert_eq!("Integer parsing error", x.description()), + } +} diff --git a/tests/lib.rs b/tests/lib.rs index e1bf7e7..3e0a088 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -2,7 +2,6 @@ extern crate rusty_secrets; use rusty_secrets::{recover_secret, generate_shares}; - #[test] fn test_reasonable_splits() { let max_shares = 50; diff --git a/tests/recovery-errors.rs b/tests/recovery-errors.rs new file mode 100644 index 0000000..f1c6012 --- /dev/null +++ b/tests/recovery-errors.rs @@ -0,0 +1,107 @@ +extern crate rusty_secrets; + +use rusty_secrets::{recover_secret}; + +#[test] +#[should_panic(expected = "Not enough shares provided!")] +fn test_recover_sellibitze_missing_share() { + let share1 = "2-1-1YAYwmOHqZ69jA".to_string(); + let shares = vec![share1]; + + recover_secret(shares).unwrap(); +} + +#[test] +#[should_panic(expected = "Not enough shares provided!")] +fn test_recover_sellibitze_no_shares() { + let shares = vec![]; + recover_secret(shares).unwrap(); +} + +#[test] +#[should_panic(expected = "Share parse error: Expected 3 parts separated by a minus sign")] +fn test_recover_2_parts_share() { + let share1 = "2-1-1YAYwmOHqZ69jA".to_string(); + let share2 = "2-F7rAjX3UOa53KA".to_string(); + + let shares = vec![share1, share2]; + + recover_secret(shares).unwrap(); +} + +#[test] +#[should_panic(expected = "Integer parsing error")] +fn test_recover_incorrect_share_num() { + let share1 = "2-1-1YAYwmOHqZ69jA".to_string(); + let share2 = "2-DEFINITLY_NAN-YJZQDGm22Y77Gw".to_string(); + + let shares = vec![share1, share2]; + + recover_secret(shares).unwrap(); +} + +#[test] +#[should_panic(expected = "Share parse error: Illegal K,N parameters")] +fn test_recover_0_share_num() { + let share1 = "2-0-1YAYwmOHqZ69jA".to_string(); + let share2 = "2-1-YJZQDGm22Y77Gw".to_string(); + + let shares = vec![share1, share2]; + + recover_secret(shares).unwrap(); +} + +#[test] +#[should_panic(expected = "Share parse error: Base64 decoding of data block failed")] +fn test_recover_invalid_b64() { + let share1 = "2-5-j0P4PHsw4lW+rg".to_string(); + let share2 = "2-1-YJZQDG((((m22Y)))77Gw".to_string(); + + let shares = vec![share1, share2]; + + recover_secret(shares).unwrap(); +} + +#[test] +#[should_panic(expected = "Incompatible shares")] +fn test_recover_invalid_b64_size() { + let share1 = "2-5-j0P4PHsw4lW+rg".to_string(); + let share2 = "2-1-YJZQDGm22Y77GwZ69jA".to_string(); + + let shares = vec![share1, share2]; + + recover_secret(shares).unwrap(); +} + +#[test] +#[should_panic(expected = "Duplicate Share Number")] +fn test_recover_duplicate_shares_number() { + let share1 = "2-1-1YAYwmOHqZ69jA".to_string(); + let share2 = "2-1-j0P4PHsw4lW+rg".to_string(); + + let shares = vec![share1, share2]; + + recover_secret(shares).unwrap(); +} + +#[test] +#[should_panic(expected = "Duplicate Share Data")] +fn test_recover_duplicate_shares_data() { + let share1 = "2-2-YJZQDGm22Y77Gw".to_string(); + let share2 = "2-3-YJZQDGm22Y77Gw".to_string(); + + let shares = vec![share1, share2]; + + recover_secret(shares).unwrap(); +} + +#[test] +#[should_panic(expected = "Not enough shares provided!")] +fn test_recover_too_few_shares() { + let share1 = "5-1-DbuicpLQiCf7bVWiAz8eCpQGpdZmYQ7z2j2+g351tWFLOQPTZkXY8BYfwGGGjkOoz1g9x0ScmLFcWk+2tign".to_string(); + let share2 = "5-2-nShdfkY5+SlfybMyqjHXCZ01bq5N/0Lkf0nQZw5x3bnHIEVfa0RA4YcJ4SjG/UdpgO/gOcyLRkSp2Dwf8bvw".to_string(); + + let shares = vec![share1, share2]; + + recover_secret(shares).unwrap(); +} diff --git a/tests/test_vectors.rs b/tests/test_vectors.rs index fcb8894..f310bc0 100644 --- a/tests/test_vectors.rs +++ b/tests/test_vectors.rs @@ -42,96 +42,3 @@ fn test_recover_sellibitze_more_than_threshold_shars() { secret.push(10); assert_eq!(recover_secret(shares).unwrap(), secret); } - -#[test] -#[should_panic] -fn test_recover_sellibitze_missing_share() { - let share1 = "2-1-1YAYwmOHqZ69jA".to_string(); - let shares = vec![share1]; - - recover_secret(shares).unwrap(); -} - -#[test] -#[should_panic] -fn test_recover_sellibitze_no_shares() { - let shares = vec![]; - recover_secret(shares).unwrap(); -} - -#[test] -#[should_panic] -fn test_recover_2_parts_share() { - let share1 = "2-1-1YAYwmOHqZ69jA".to_string(); - let share2 = "2-F7rAjX3UOa53KA".to_string(); - - let shares = vec![share1, share2]; - - recover_secret(shares).unwrap(); -} - -#[test] -#[should_panic] -fn test_recover_incorrect_share_num() { - let share1 = "2-1-1YAYwmOHqZ69jA".to_string(); - let share2 = "2-DEFINITLY_NAN-YJZQDGm22Y77Gw".to_string(); - - let shares = vec![share1, share2]; - - recover_secret(shares).unwrap(); -} - -#[test] -#[should_panic] -fn test_recover_0_share_num() { - let share1 = "2-0-1YAYwmOHqZ69jA".to_string(); - let share2 = "2-1-YJZQDGm22Y77Gw".to_string(); - - let shares = vec![share1, share2]; - - recover_secret(shares).unwrap(); -} - -#[test] -#[should_panic] -fn test_recover_invalid_b64() { - let share1 = "2-0-1YAYwmOHqZ69jA".to_string(); - let share2 = "2-1-YJZQDG((((m22Y)))77Gw".to_string(); - - let shares = vec![share1, share2]; - - recover_secret(shares).unwrap(); -} - -#[test] -#[should_panic] -fn test_recover_invalid_b64_size() { - let share1 = "2-0-1YAYwmOHqZ69jA".to_string(); - let share2 = "2-1-YJZQDGm22Y77GwZ69jA".to_string(); - - let shares = vec![share1, share2]; - - recover_secret(shares).unwrap(); -} - -#[test] -#[should_panic] -fn test_recover_duplicate_shares() { - let share1 = "2-0-1YAYwmOHqZ69jA".to_string(); - let share2 = "2-0-1YAYwmOHqZ69jA".to_string(); - - let shares = vec![share1, share2]; - - recover_secret(shares).unwrap(); -} - -#[test] -#[should_panic] -fn test_recover_too_few_shares() { - let share1 = "5-1-DbuicpLQiCf7bVWiAz8eCpQGpdZmYQ7z2j2+g351tWFLOQPTZkXY8BYfwGGGjkOoz1g9x0ScmLFcWk+2tign".to_string(); - let share2 = "5-2-nShdfkY5+SlfybMyqjHXCZ01bq5N/0Lkf0nQZw5x3bnHIEVfa0RA4YcJ4SjG/UdpgO/gOcyLRkSp2Dwf8bvw".to_string(); - - let shares = vec![share1, share2]; - - recover_secret(shares).unwrap(); -}