New share format. (#13)

* Removing Coverall support until fixed. (See #12)

* Changing Cargo license.

* New wrapping of share data to support signatures.
This commit is contained in:
Frederic Jacobs
2016-11-18 12:34:06 +01:00
committed by GitHub
parent 6ad30652a6
commit aeb8e4c21f
14 changed files with 454 additions and 244 deletions

View File

@ -1,18 +1,11 @@
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:
- |
travis-cargo build &&
travis-cargo test
after_success:
- travis-cargo coveralls --no-sudo
rust:
- stable
- beta
- nightly
matrix:
allow_failures:
- rust: nightly

23
Cargo.lock generated
View File

@ -1,20 +1,20 @@
[root]
name = "rusty_secrets"
version = "0.0.2"
version = "0.0.3"
dependencies = [
"getopts 0.2.14 (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.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "getopts"
version = "0.2.14"
name = "libc"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.9"
name = "protobuf"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -22,11 +22,16 @@ name = "rand"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-serialize"
version = "0.3.18"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8"
"checksum protobuf 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "6ec4c2fe04370298218a09ab53a534febf54c160c5554e4de987b6d73c916d5d"
"checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5"
"checksum rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)" = "bff9fc1c79f2dec76b253273d07682e94a978bd8f132ded071188122b2af9818"

View File

@ -1,24 +1,14 @@
[package]
name = "rusty_secrets"
version = "0.0.2"
version = "0.0.3"
authors = ["Frederic Jacobs <github@fredericjacobs.com>", "sellibitze"]
description = "Implementation of threshold Shamir secret sharing in the Rust programming language."
homepage = "https://github.com/freedomofpress/RustySecrets"
license = "GPL-3.0"
license = "BSD-3-Clause"
readme = "README.md"
build = "build.rs"
[dependencies]
getopts = "^0.2.14"
rustc-serialize = "^0.3.18"
rand = "^0.3.14"
[lib]
name = "rusty_secrets"
path = "src/lib/mod.rs"
crate_type = ["rlib"]
[[bin]]
name = "rusty_secrets_bin"
path = "src/main.rs"
doc = false
[dependencies.protobuf]

View File

@ -49,27 +49,7 @@ A share is built out of three parts separated with a dash: K-N-D.
- N is the identifier of the share and varies between 1 and n where n is the total number of generated shares.
- The D part is a Base64 encoding of a specific share's raw data.
### Command-line encoding
Passing a secret to rustysecrets for encoding:
```
$ echo My secret | ./rusty_secrets_bin -e2,5
2-1-1YAYwmOHqZ69jA
2-2-YJZQDGm22Y77Gw
2-3-+G9ovW9SAnUynQ
2-4-F7rAjX3UOa53KA
2-5-j0P4PHsw4lW+rg
```
The parameters following the `-e` option tell rustysecrets to create 5 shares of which 2 will be necessary for decoding.
Decoding a subset of shares (one share per line) can be done like this:
```
$ echo -e "2-2-YJZQDGm22Y77Gw \n 2-4-F7rAjX3UOa53KA" | ./rusty_secrets_bin -d
My secret
```
// TODO: Rewrite documentation to match current implementation
## Vocabulary

8
protobuf/ShareData.proto Normal file
View File

@ -0,0 +1,8 @@
syntax = "proto3";
message ShareData {
bytes shamirData = 1;
bytes signature = 2;
bytes proof = 3;
}

356
src/ShareDataMod.rs Normal file
View File

@ -0,0 +1,356 @@
// This file is generated. Do not edit
// @generated
// https://github.com/Manishearth/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy)]
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unsafe_code)]
#![allow(unused_imports)]
#![allow(unused_results)]
use protobuf::Message as Message_imported_for_functions;
use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions;
#[derive(Clone,Default)]
pub struct ShareData {
// message fields
shamirData: ::protobuf::SingularField<::std::vec::Vec<u8>>,
signature: ::protobuf::SingularField<::std::vec::Vec<u8>>,
proof: ::protobuf::SingularField<::std::vec::Vec<u8>>,
// special fields
unknown_fields: ::protobuf::UnknownFields,
cached_size: ::std::cell::Cell<u32>,
}
// see codegen.rs for the explanation why impl Sync explicitly
unsafe impl ::std::marker::Sync for ShareData {}
impl ShareData {
pub fn new() -> ShareData {
::std::default::Default::default()
}
pub fn default_instance() -> &'static ShareData {
static mut instance: ::protobuf::lazy::Lazy<ShareData> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ShareData,
};
unsafe {
instance.get(|| {
ShareData {
shamirData: ::protobuf::SingularField::none(),
signature: ::protobuf::SingularField::none(),
proof: ::protobuf::SingularField::none(),
unknown_fields: ::protobuf::UnknownFields::new(),
cached_size: ::std::cell::Cell::new(0),
}
})
}
}
// optional bytes shamirData = 1;
pub fn clear_shamirData(&mut self) {
self.shamirData.clear();
}
pub fn has_shamirData(&self) -> bool {
self.shamirData.is_some()
}
// Param is passed by value, moved
pub fn set_shamirData(&mut self, v: ::std::vec::Vec<u8>) {
self.shamirData = ::protobuf::SingularField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_shamirData(&mut self) -> &mut ::std::vec::Vec<u8> {
if self.shamirData.is_none() {
self.shamirData.set_default();
};
self.shamirData.as_mut().unwrap()
}
// Take field
pub fn take_shamirData(&mut self) -> ::std::vec::Vec<u8> {
self.shamirData.take().unwrap_or_else(|| ::std::vec::Vec::new())
}
pub fn get_shamirData(&self) -> &[u8] {
match self.shamirData.as_ref() {
Some(v) => &v,
None => &[],
}
}
// optional bytes signature = 2;
pub fn clear_signature(&mut self) {
self.signature.clear();
}
pub fn has_signature(&self) -> bool {
self.signature.is_some()
}
// Param is passed by value, moved
pub fn set_signature(&mut self, v: ::std::vec::Vec<u8>) {
self.signature = ::protobuf::SingularField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_signature(&mut self) -> &mut ::std::vec::Vec<u8> {
if self.signature.is_none() {
self.signature.set_default();
};
self.signature.as_mut().unwrap()
}
// Take field
pub fn take_signature(&mut self) -> ::std::vec::Vec<u8> {
self.signature.take().unwrap_or_else(|| ::std::vec::Vec::new())
}
pub fn get_signature(&self) -> &[u8] {
match self.signature.as_ref() {
Some(v) => &v,
None => &[],
}
}
// optional bytes proof = 3;
pub fn clear_proof(&mut self) {
self.proof.clear();
}
pub fn has_proof(&self) -> bool {
self.proof.is_some()
}
// Param is passed by value, moved
pub fn set_proof(&mut self, v: ::std::vec::Vec<u8>) {
self.proof = ::protobuf::SingularField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_proof(&mut self) -> &mut ::std::vec::Vec<u8> {
if self.proof.is_none() {
self.proof.set_default();
};
self.proof.as_mut().unwrap()
}
// Take field
pub fn take_proof(&mut self) -> ::std::vec::Vec<u8> {
self.proof.take().unwrap_or_else(|| ::std::vec::Vec::new())
}
pub fn get_proof(&self) -> &[u8] {
match self.proof.as_ref() {
Some(v) => &v,
None => &[],
}
}
}
impl ::protobuf::Message for ShareData {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream) -> ::protobuf::ProtobufResult<()> {
while !try!(is.eof()) {
let (field_number, wire_type) = try!(is.read_tag_unpack());
match field_number {
1 => {
try!(::protobuf::rt::read_singular_bytes_into(wire_type, is, &mut self.shamirData));
},
2 => {
try!(::protobuf::rt::read_singular_bytes_into(wire_type, is, &mut self.signature));
},
3 => {
try!(::protobuf::rt::read_singular_bytes_into(wire_type, is, &mut self.proof));
},
_ => {
try!(::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields()));
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
for value in &self.shamirData {
my_size += ::protobuf::rt::bytes_size(1, &value);
};
for value in &self.signature {
my_size += ::protobuf::rt::bytes_size(2, &value);
};
for value in &self.proof {
my_size += ::protobuf::rt::bytes_size(3, &value);
};
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream) -> ::protobuf::ProtobufResult<()> {
if let Some(v) = self.shamirData.as_ref() {
try!(os.write_bytes(1, &v));
};
if let Some(v) = self.signature.as_ref() {
try!(os.write_bytes(2, &v));
};
if let Some(v) = self.proof.as_ref() {
try!(os.write_bytes(3, &v));
};
try!(os.write_unknown_fields(self.get_unknown_fields()));
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn type_id(&self) -> ::std::any::TypeId {
::std::any::TypeId::of::<ShareData>()
}
fn as_any(&self) -> &::std::any::Any {
self as &::std::any::Any
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
::protobuf::MessageStatic::descriptor_static(None::<Self>)
}
}
impl ::protobuf::MessageStatic for ShareData {
fn new() -> ShareData {
ShareData::new()
}
fn descriptor_static(_: ::std::option::Option<ShareData>) -> &'static ::protobuf::reflect::MessageDescriptor {
static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::reflect::MessageDescriptor,
};
unsafe {
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_singular_bytes_accessor(
"shamirData",
ShareData::has_shamirData,
ShareData::get_shamirData,
));
fields.push(::protobuf::reflect::accessor::make_singular_bytes_accessor(
"signature",
ShareData::has_signature,
ShareData::get_signature,
));
fields.push(::protobuf::reflect::accessor::make_singular_bytes_accessor(
"proof",
ShareData::has_proof,
ShareData::get_proof,
));
::protobuf::reflect::MessageDescriptor::new::<ShareData>(
"ShareData",
fields,
file_descriptor_proto()
)
})
}
}
}
impl ::protobuf::Clear for ShareData {
fn clear(&mut self) {
self.clear_shamirData();
self.clear_signature();
self.clear_proof();
self.unknown_fields.clear();
}
}
impl ::std::cmp::PartialEq for ShareData {
fn eq(&self, other: &ShareData) -> bool {
self.shamirData == other.shamirData &&
self.signature == other.signature &&
self.proof == other.proof &&
self.unknown_fields == other.unknown_fields
}
}
impl ::std::fmt::Debug for ShareData {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
static file_descriptor_proto_data: &'static [u8] = &[
0x0a, 0x0f, 0x53, 0x68, 0x61, 0x72, 0x65, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x22, 0x5f, 0x0a, 0x09, 0x53, 0x68, 0x61, 0x72, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1e,
0x0a, 0x0a, 0x73, 0x68, 0x61, 0x6d, 0x69, 0x72, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x0a, 0x73, 0x68, 0x61, 0x6d, 0x69, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1c,
0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x14, 0x0a, 0x05,
0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x72, 0x6f,
0x6f, 0x66, 0x4a, 0xfc, 0x01, 0x0a, 0x06, 0x12, 0x04, 0x00, 0x00, 0x06, 0x01, 0x0a, 0x08, 0x0a,
0x01, 0x0c, 0x12, 0x03, 0x00, 0x00, 0x12, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x02,
0x00, 0x06, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x02, 0x08, 0x11, 0x0a,
0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x03, 0x08, 0x1d, 0x0a, 0x0d, 0x0a, 0x05,
0x04, 0x00, 0x02, 0x00, 0x04, 0x12, 0x04, 0x03, 0x08, 0x02, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x03, 0x08, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02,
0x00, 0x01, 0x12, 0x03, 0x03, 0x0e, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03,
0x12, 0x03, 0x03, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x04,
0x08, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x04, 0x12, 0x04, 0x04, 0x08, 0x03,
0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x04, 0x08, 0x0d, 0x0a,
0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x04, 0x0e, 0x17, 0x0a, 0x0c, 0x0a,
0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x04, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04,
0x00, 0x02, 0x02, 0x12, 0x03, 0x05, 0x08, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02,
0x04, 0x12, 0x04, 0x05, 0x08, 0x04, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x05,
0x12, 0x03, 0x05, 0x08, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03,
0x05, 0x0e, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x05, 0x1b,
0x1c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
];
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::descriptor::FileDescriptorProto,
};
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
unsafe {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}
}

View File

@ -1,19 +1,24 @@
extern crate protobuf;
extern crate rustc_serialize as serialize;
extern crate rand;
use self::rand::{ Rng, OsRng };
use self::serialize::base64::{ self, FromBase64, ToBase64 };
use rand::{ Rng, OsRng };
use serialize::base64::{ self, FromBase64, ToBase64 };
mod gf256;
use self::gf256::Gf256;
use gf256::Gf256;
use std::io;
use std::iter::repeat;
/// Generate generic errors that typeset with `io::Error`.
pub mod custom_error;
use self::custom_error::*;
use custom_error::*;
use protobuf::Message;
mod ShareDataMod;
pub use ShareDataMod::{ShareData};
/// Performs threshold k-out-of-n Shamir secret sharing.
///
@ -30,7 +35,6 @@ use self::custom_error::*;
/// Err(_) => {}// Deal with error}
/// }
/// ```
pub fn generate_shares(k: u8, n: u8, secret: &[u8]) -> io::Result<Vec<String>> {
if k > n {
return Err(other_io_err("Threshold K can not be larger than N", None));
@ -44,8 +48,11 @@ pub fn generate_shares(k: u8, n: u8, secret: &[u8]) -> io::Result<Vec<String>> {
let mut result = Vec::with_capacity(n as usize);
for (index, share) in shares.iter().enumerate() {
let b64_share = share.to_base64(config);
for (index, share) in shares.into_iter().enumerate() {
let mut share_protobuf = ShareData::new();
share_protobuf.set_shamirData(share);
let b64_share = share_protobuf.write_to_bytes().unwrap().to_base64(config);
let string = format!("{}-{}-{}", k, index+1, b64_share);
result.push(string);
}
@ -73,10 +80,16 @@ fn process_shares(shares_strings: Vec<String>) -> io::Result<(u8, Vec<(u8, Vec<u
if k < 1 || n < 1 {
return Err(other_io_err("Share parse error: Illegal K,N parameters", None));
}
let data = try!(
let raw_data = try!(
p3.from_base64().map_err(|_| other_io_err(
"Share parse error: Base64 decoding of data block failed", None ))
);
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 )));
let data = Vec::from(protobuf_data.get_shamirData());
if let Some((ck, cl)) = opt_k_l {
if ck != k || cl != data.len() {
return Err(other_io_err("Incompatible shares", None));
@ -110,8 +123,8 @@ fn process_shares(shares_strings: Vec<String>) -> io::Result<(u8, Vec<(u8, Vec<u
///
/// ```
/// use rusty_secrets::{recover_secret};
/// let share1 = "2-1-1YAYwmOHqZ69jA".to_string();
/// let share2 = "2-4-F7rAjX3UOa53KA".to_string();
/// let share1 = "2-1-Cha7s14Q/mSwWko0ittr+/Uf79RHQMIP".to_string();
/// let share2 = "2-4-ChaydsUJDypD9ZWxwvIICh/cmZvzusOF".to_string();
/// let shares = vec![share1, share2];
///
/// match recover_secret(shares) {

View File

@ -1,148 +0,0 @@
extern crate getopts;
use getopts::Options;
use lib::custom_error::*;
mod lib;
use std::io;
use std::env;
use std::process;
enum Action {
Encode(u8, u8), // k and n parameter
Decode
}
// a try!-like macro for Option<T> expressions that takes
// a &'static str as error message as 2nd parameter
// and creates an Error out of it if necessary.
macro_rules! otry {
($o:expr, $e:expr) => (
match $o {
Some(thing_) => thing_,
None => return Err(convert::From::from(Error::new($e, None)))
}
)
}
fn main() {
let mut stderr = io::stderr();
let args: Vec<String> = env::args().collect();
let mut opts = Options::new();
opts.optflag("h", "help", "print this help text");
opts.optflag("d", "decode", "for decoding");
opts.optopt("e", "encode", "for encoding, K is the required number of \
shares for decoding, N is the number of shares \
to generate. 1 <= K <= N <= 255", "K,N");
let opt_matches = match opts.parse(&args[1..]) {
Ok(m) => m,
Err(f) => {
drop(writeln!(&mut stderr, "Error: {}", f));
process::exit(1);
}
};
if args.len() < 2 || opt_matches.opt_present("h") {
println!(
"The program rustysecrets is an implementation of Shamir's secret sharing scheme.\n\
It is applied byte-wise within a finite field for arbitrarily long secrets.\n");
println!("{}", opts.usage("Usage: rustysecrets [options]"));
println!("Input is read from STDIN and output is written to STDOUT.");
return;
}
let action: Result<_,_> =
match (opt_matches.opt_present("e"), opt_matches.opt_present("d")) {
(false, false) => Err("Nothing to do! Use -e or -d"),
(true, true) => Err("Use either -e or -d and not both"),
(false, true) => Ok(Action::Decode),
(true, false) => {
if let Some(param) = opt_matches.opt_str("e") {
if let Ok((k,n)) = parse_k_n(&*param) {
if 0 < k && k <= n {
Ok(Action::Encode(k,n))
} else {
Err("Invalid encoding parameters K,N")
}
} else {
Err("Could not parse K,N parameters")
}
} else {
Err("No parameter for -e or -d provided")
}
}
};
let result =
match action {
Ok(Action::Encode(k,n)) => perform_encode_from_io(k, n),
Ok(Action::Decode) => perform_decode_from_io(),
Err(e) => Err(other_io_err(e, None))
};
if let Err(e) = result {
drop(writeln!(&mut stderr, "{}", e));
process::exit(1);
}
}
fn perform_encode_from_io(k: u8, n: u8) -> io::Result<()> {
let secret = {
let limit: usize = 0x10000;
let stdin = io::stdin();
let mut locked = stdin.lock();
let mut tmp: Vec<u8> = Vec::new();
try!(locked.by_ref().take(limit as u64).read_to_end(&mut tmp));
if tmp.len() == limit {
let mut dummy = [0u8];
if try!(locked.read(&mut dummy)) > 0 {
return Err(other_io_err("Secret too large",
Some(format!("My limit is at {} bytes.", limit))));
}
}
tmp.pop();
tmp
};
match lib::generate_shares(k, n, &secret) {
Ok(shares) => {
for share in shares {println!("{:?}", share)};
}
Err(e) => { return Err(e) as io::Result<()>; }
}
return Ok(()) as io::Result<()>;
}
fn perform_decode_from_io() -> io::Result<()> {
let stdin = io::stdin();
let stdin = io::BufReader::new(stdin.lock());
let mut shares: Vec<String> = Vec::new();
for line in stdin.lines() {
match line {
Ok(share) => shares.push(share),
Err(_) => {}
}
}
return match lib::recover_secret(shares) {
Ok(secret) => {
let mut out = io::stdout();
try!(out.write_all(&*secret));
out.flush()
}
Err(e) => {Err(e) as io::Result<()>}
};
}
fn parse_k_n(s: &str) -> io::Result<(u8, u8)> {
let mut iter = s.split(',');
let msg = "K and N have to be separated with a comma";
let s1 = otry!(iter.next(), msg).trim();
let s2 = otry!(iter.next(), msg).trim();
let k = try!(s1.parse().map_err(pie2io));
let n = try!(s2.parse().map_err(pie2io));
Ok((k, n))
}

View File

@ -2,15 +2,6 @@ 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() {
@ -21,8 +12,8 @@ fn test_recover_sellibitze_no_shares() {
#[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 share1 = "2-1-CgmKQZHMO+5n5pU".to_string();
let share2 = "2-2".to_string();
let shares = vec![share1, share2];
@ -32,8 +23,8 @@ fn test_recover_2_parts_share() {
#[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 share1 = "2-1-CgnlCxRNtnkzENE".to_string();
let share2 = "2-DEFINITLY_NAN-CgkAnUgP3lfwjyM".to_string();
let shares = vec![share1, share2];
@ -54,7 +45,7 @@ fn test_recover_0_share_num() {
#[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 share1 = "2-1-CgnlCxRNtnkzENE".to_string();
let share2 = "2-1-YJZQDG((((m22Y)))77Gw".to_string();
let shares = vec![share1, share2];
@ -65,8 +56,8 @@ fn test_recover_invalid_b64() {
#[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 share1 = "2-1-CgnlCxRNtnkzENE".to_string();
let share2 = "3-5-Cgl/3sS13REq2X8".to_string();
let shares = vec![share1, share2];
@ -76,8 +67,8 @@ fn test_recover_invalid_b64_size() {
#[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 share1 = "2-1-CgnlCxRNtnkzENE".to_string();
let share2 = "2-1-CgkAnUgP3lfwjyM".to_string();
let shares = vec![share1, share2];
@ -87,8 +78,8 @@ fn test_recover_duplicate_shares_number() {
#[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 share1 = "2-1-CgnlCxRNtnkzENE".to_string();
let share2 = "2-2-CgnlCxRNtnkzENE".to_string();
let shares = vec![share1, share2];
@ -98,8 +89,8 @@ fn test_recover_duplicate_shares_data() {
#[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 share1 = "3-1-ChbcCdSZOaMn6DM1jFca2P6/0WRlP7AK".to_string();
let share2 = "3-2-ChbG46L1zRszs0PPn63XnnupmZTcgYJ3".to_string();
let shares = vec![share1, share2];

View File

@ -1,13 +1,35 @@
extern crate rusty_secrets;
extern crate protobuf;
extern crate rustc_serialize as serialize;
use serialize::base64::{ self, FromBase64, ToBase64 };
use rusty_secrets::{recover_secret};
use protobuf::Message;
#[cfg(test)]
use rusty_secrets::{recover_secret, ShareData};
pub fn wrap_from_sellibitze(share: &str) -> String {
let parts: Vec<_> = share.trim().split('-').collect();
let share_data = parts[2].from_base64().unwrap();
let config = base64::Config {
pad: false,
..base64::STANDARD
};
let mut share_protobuf = ShareData::new();
share_protobuf.set_shamirData(share_data);
let b64_share = share_protobuf.write_to_bytes().unwrap().to_base64(config);
format!("{}-{}-{}", parts[0], parts[1], b64_share)
}
#[test]
fn test_recover_sellibitze() {
let share1 = "2-1-1YAYwmOHqZ69jA".to_string();
let share2 = "2-4-F7rAjX3UOa53KA".to_string();
let share1 = "2-1-1YAYwmOHqZ69jA";
let share2 = "2-4-F7rAjX3UOa53KA";
let shares = vec![share1, share2];
let shares = vec![share1, share2].iter().map(|x| wrap_from_sellibitze(x)).collect::<Vec<_>>();
let mut secret = "My secret".to_string().into_bytes();
secret.push(10);
@ -23,7 +45,7 @@ fn test_recover_es_test_vectors() {
let share4 = "5-6-yyVPUeaYPPiWK0wIV5OQ/t61V0lSEO+7X++EWeHRlIq3sRBNwUpKNfx/C+Vc9xTzUftrqBKvkWDZQal7nyi2".to_string();
let share5 = "5-7-i8iL6bVf272B3qIjp0QqSny6AIm+DkP7oQjkVVLvx9EMhlvd4HJOxPpmtNF/RjA/zz21d7DY/B//saOPpBQa".to_string();
let shares = vec![share1, share2, share3, share4, share5];
let shares = vec![share1, share2, share3, share4, share5].iter().map(|x| wrap_from_sellibitze(x)).collect::<Vec<_>>();
let secret = "The immoral cannot be made moral through the use of secret law.".to_string().into_bytes();
assert_eq!(recover_secret(shares).unwrap(), secret);
@ -36,7 +58,7 @@ fn test_recover_sellibitze_more_than_threshold_shars() {
let share3 = "2-2-YJZQDGm22Y77Gw".to_string();
let share4 = "2-5-j0P4PHsw4lW+rg".to_string();
let shares = vec![share1, share2, share3, share4];
let shares = vec![share1, share2, share3, share4].iter().map(|x| wrap_from_sellibitze(x)).collect::<Vec<_>>();
let mut secret = "My secret".to_string().into_bytes();
secret.push(10);