From 8c37b2f6293e49b47bd4209430f05cde997e4a76 Mon Sep 17 00:00:00 2001 From: Frederic Jacobs Date: Sat, 2 Apr 2016 13:06:26 +0200 Subject: [PATCH] Part 1 of #1. Refactoring to separate lib from command line. --- Cargo.lock | 5 +- Cargo.toml | 16 +- README.md | 7 +- src/lib/custom_error.rs | 44 ++++++ src/{ => lib}/gf256.rs | 1 - src/lib/mod.rs | 111 ++++++++++++++ src/main.rs | 317 +++++++++++++--------------------------- 7 files changed, 277 insertions(+), 224 deletions(-) create mode 100644 src/lib/custom_error.rs rename src/{ => lib}/gf256.rs (99%) create mode 100644 src/lib/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 580d5ba..5f4e7b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,8 +1,9 @@ [root] -name = "secretshare" -version = "0.1.6" +name = "rusty-secrets" +version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.9 (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)", ] diff --git a/Cargo.toml b/Cargo.toml index 6f9225c..071a296 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,7 @@ [package] -name = "secretshare" -version = "0.1.6" -authors = ["Sebastian Gesemann "] -description = "This is an implementation of Shamir's secret sharing scheme." +name = "rusty-secrets" +version = "0.0.1" +description = "Implementation of threshold Shamir secret sharing in the Rust programming language." license = "GPLv3" readme = "README.md" @@ -10,3 +9,12 @@ readme = "README.md" getopts = "^0.2.14" rustc-serialize = "^0.3.18" rand = "^0.3.14" +libc = "^0.2.0" + +[lib] +name = "rusty_secrets_lib" +path = "src/lib/mod.rs" + +[[bin]] +name = "rusty_secrets_bin" +path = "src/main.rs" diff --git a/README.md b/README.md index c64d4a7..c433160 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# secretshare +# Rusty Secrets This program is an implementation of [Shamir's secret sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing). @@ -107,3 +107,8 @@ field I used is the same as the one you can find in the RAID 6 implementation of the Linux kernel or the Anubis block cipher: Gf(2^8) reduction polynomial is x^8 + x^4 + x^3 + x^2 + 1 or alternatively 11D in hex. + +## Credits + +Rusty Secrets was forked off [sellibitze's secretshare.](https://github.com/sellibitze/secretshare) + diff --git a/src/lib/custom_error.rs b/src/lib/custom_error.rs new file mode 100644 index 0000000..3edafc5 --- /dev/null +++ b/src/lib/custom_error.rs @@ -0,0 +1,44 @@ +pub use std::convert; +pub use std::io::prelude::*; + +use std::error; +use std::fmt; +use std::io; + +#[derive(Debug)] +pub struct Error { + descr: &'static str, + detail: Option, +} + +impl Error { + pub fn new(descr: &'static str, detail: Option) -> Error { + Error { descr: descr, detail: detail } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.detail { + None => write!(f, "{}", self.descr), + Some(ref detail) => write!(f, "{} ({})", self.descr, detail) + } + } +} + +impl error::Error for Error { + fn description(&self) -> &str { self.descr } + fn cause(&self) -> Option<&error::Error> { None } +} + +impl convert::From for io::Error { + fn from(me: Error) -> io::Error { + io::Error::new(io::ErrorKind::Other, me) + } +} + +pub fn other_io_err(descr: &'static str, detail: Option) -> io::Error { + convert::From::from( + Error::new(descr, detail) + ) +} diff --git a/src/gf256.rs b/src/lib/gf256.rs similarity index 99% rename from src/gf256.rs rename to src/lib/gf256.rs index f84bcf5..8c026a2 100644 --- a/src/gf256.rs +++ b/src/lib/gf256.rs @@ -140,4 +140,3 @@ impl Div for Gf256 { } } } - diff --git a/src/lib/mod.rs b/src/lib/mod.rs new file mode 100644 index 0000000..46dbfca --- /dev/null +++ b/src/lib/mod.rs @@ -0,0 +1,111 @@ +extern crate rustc_serialize as serialize; +extern crate rand; + +use self::rand::{ Rng, OsRng }; +pub use self::serialize::base64::{ self, FromBase64, ToBase64 }; + +mod gf256; +use self::gf256::Gf256; + +use std::io; +pub use std::str; +use std::iter::repeat; + +pub mod custom_error; +use self::custom_error::*; + +fn new_vec(n: usize, x: T) -> Vec { + repeat(x).take(n).collect() +} + +/// evaluates a polynomial at x=1, 2, 3, ... n (inclusive) +fn encode(src: &[u8], n: u8, w: &mut W) -> io::Result<()> { + for raw_x in 1 .. ((n as u16) + 1) { + let x = Gf256::from_byte(raw_x as u8); + let mut fac = Gf256::one(); + let mut acc = Gf256::zero(); + for &coeff in src.iter() { + acc = acc + fac * Gf256::from_byte(coeff); + fac = fac * x; + } + try!(w.write(&[acc.to_byte()])); + } + Ok(()) +} + +/// evaluates an interpolated polynomial at `raw_x` where +/// the polynomial is determined using Lagrangian interpolation +/// based on the given x/y coordinates `src`. +fn lagrange_interpolate(src: &[(u8, u8)], raw_x: u8) -> u8 { + let x = Gf256::from_byte(raw_x); + let mut sum = Gf256::zero(); + for (i, &(raw_xi, raw_yi)) in src.iter().enumerate() { + let xi = Gf256::from_byte(raw_xi); + let yi = Gf256::from_byte(raw_yi); + let mut lix = Gf256::one(); + for (j, &(raw_xj, _)) in src.iter().enumerate() { + if i != j { + let xj = Gf256::from_byte(raw_xj); + let delta = xi - xj; + assert!(delta.poly !=0, "Duplicate shares"); + lix = lix * (x - xj) / delta; + } + } + sum = sum + lix * yi; + } + sum.to_byte() +} + +fn secret_share(src: &[u8], k: u8, n: u8) -> io::Result>> { + let mut result = Vec::with_capacity(n as usize); + for _ in 0 .. (n as usize) { + result.push(new_vec(src.len(), 0u8)); + } + let mut col_in = new_vec(k as usize, 0u8); + let mut col_out = Vec::with_capacity(n as usize); + let mut osrng = try!(OsRng::new()); + for (c, &s) in src.iter().enumerate() { + col_in[0] = s; + osrng.fill_bytes(&mut col_in[1..]); + col_out.clear(); + try!(encode(&*col_in, n, &mut col_out)); + for (&y, share) in col_out.iter().zip(result.iter_mut()) { + share[c] = y; + } + } + Ok(result) +} + +pub fn perform_encode(k: u8, n: u8, secret: Vec) -> io::Result>> { + let shares = try!(secret_share(&*secret, k, n)); + let config = base64::Config { + pad: false, + ..base64::STANDARD + }; + + let mut result = Vec::with_capacity(n as usize); + + for (index, share) in shares.iter().enumerate() { + let salad = share.to_base64(config); + let string = format!("{}-{}-{}", k, index+1, salad).into_bytes(); + result.push(string); + } + + Ok(result) +} + +pub fn perform_decode(k: u8, shares: Vec<(u8,Vec)>) -> io::Result> { + assert!(!shares.is_empty()); + let slen = shares[0].1.len(); + let mut col_in = Vec::with_capacity(k as usize); + let mut secret = Vec::with_capacity(slen); + for byteindex in 0 .. slen { + col_in.clear(); + for s in shares.iter().take(k as usize) { + col_in.push((s.0, s.1[byteindex])); + } + secret.push(lagrange_interpolate(&*col_in, 0u8)); + } + + return Ok(secret) as io::Result>; +} diff --git a/src/main.rs b/src/main.rs index 30fe96c..5146bf3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,58 +1,19 @@ -extern crate rustc_serialize as serialize; extern crate getopts; -extern crate rand; -use std::convert; -use std::env; -use std::error; -use std::fmt; +use getopts::Options; + +use std::str; +use lib::custom_error::*; +use lib::serialize::base64::{ FromBase64 }; +mod lib; + use std::io; -use std::io::prelude::*; -use std::iter::repeat; +use std::env; use std::num; -use rand::{ Rng, OsRng }; -use getopts::Options; -use serialize::base64::{ self, FromBase64, ToBase64 }; - -use gf256::Gf256; - -mod gf256; - -fn new_vec(n: usize, x: T) -> Vec { - repeat(x).take(n).collect() -} - -#[derive(Debug)] -pub struct Error { - descr: &'static str, - detail: Option, -} - -impl Error { - fn new(descr: &'static str, detail: Option) -> Error { - Error { descr: descr, detail: detail } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.detail { - None => write!(f, "{}", self.descr), - Some(ref detail) => write!(f, "{} ({})", self.descr, detail) - } - } -} - -impl error::Error for Error { - fn description(&self) -> &str { self.descr } - fn cause(&self) -> Option<&error::Error> { None } -} - -impl convert::From for io::Error { - fn from(me: Error) -> io::Error { - io::Error::new(io::ErrorKind::Other, me) - } +enum Action { + Encode(u8, u8), // k and n parameter + Decode } // a try!-like macro for Option expressions that takes @@ -67,94 +28,71 @@ macro_rules! otry { ) } -/// maps a ParseIntError to an io::Error -fn pie2io(p: num::ParseIntError) -> io::Error { - convert::From::from( - Error::new("Integer parsing error", Some(p.to_string())) - ) -} +fn main() { + let mut stderr = io::stderr(); + let args: Vec = env::args().collect(); -fn other_io_err(descr: &'static str, detail: Option) -> io::Error { - convert::From::from( - Error::new(descr, detail) - ) -} - -/// evaluates a polynomial at x=1, 2, 3, ... n (inclusive) -fn encode(src: &[u8], n: u8, w: &mut W) -> io::Result<()> { - for raw_x in 1 .. ((n as u16) + 1) { - let x = Gf256::from_byte(raw_x as u8); - let mut fac = Gf256::one(); - let mut acc = Gf256::zero(); - for &coeff in src.iter() { - acc = acc + fac * Gf256::from_byte(coeff); - fac = fac * x; + 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)); + // env::set_exit_status(1); // FIXME: unstable feature + return; } - try!(w.write(&[acc.to_byte()])); - } - Ok(()) -} + }; -/// evaluates an interpolated polynomial at `raw_x` where -/// the polynomial is determined using Lagrangian interpolation -/// based on the given x/y coordinates `src`. -fn lagrange_interpolate(src: &[(u8, u8)], raw_x: u8) -> u8 { - let x = Gf256::from_byte(raw_x); - let mut sum = Gf256::zero(); - for (i, &(raw_xi, raw_yi)) in src.iter().enumerate() { - let xi = Gf256::from_byte(raw_xi); - let yi = Gf256::from_byte(raw_yi); - let mut lix = Gf256::one(); - for (j, &(raw_xj, _)) in src.iter().enumerate() { - if i != j { - let xj = Gf256::from_byte(raw_xj); - let delta = xi - xj; - assert!(delta.poly !=0, "Duplicate shares"); - lix = lix * (x - xj) / delta; + if args.len() < 2 || opt_matches.opt_present("h") { + println!( +"The program secretshare 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") + } } - } - sum = sum + lix * yi; + }; + + 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)); + // env::set_exit_status(1); // FIXME: unstable feature } - sum.to_byte() } -fn secret_share(src: &[u8], k: u8, n: u8) -> io::Result>> { - let mut result = Vec::with_capacity(n as usize); - for _ in 0 .. (n as usize) { - result.push(new_vec(src.len(), 0u8)); - } - let mut col_in = new_vec(k as usize, 0u8); - let mut col_out = Vec::with_capacity(n as usize); - let mut osrng = try!(OsRng::new()); - for (c, &s) in src.iter().enumerate() { - col_in[0] = s; - osrng.fill_bytes(&mut col_in[1..]); - col_out.clear(); - try!(encode(&*col_in, n, &mut col_out)); - for (&y, share) in col_out.iter().zip(result.iter_mut()) { - share[c] = y; - } - } - Ok(result) -} - -enum Action { - Encode(u8, u8), // k and n parameter - Decode -} - -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)) -} - -fn perform_encode(k: u8, n: u8) -> io::Result<()> { - let secret = { +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(); @@ -169,16 +107,14 @@ fn perform_encode(k: u8, n: u8) -> io::Result<()> { } tmp }; - let shares = try!(secret_share(&*secret, k, n)); - let config = base64::Config { - pad: false, - ..base64::STANDARD - }; - for (index, share) in shares.iter().enumerate() { - let salad = share.to_base64(config); - println!("{}-{}-{}", k, index+1, salad); + match lib::perform_encode(k, n, secret) { + Ok(shares) => { + for share in shares {println!("{:?}", str::from_utf8(&share).unwrap())}; + } + Err(e) => { return Err(e) as io::Result<()>; } } - Ok(()) + + return Ok(()) as io::Result<()>; } /// reads shares from stdin and returns Ok(k, shares) on success @@ -195,7 +131,7 @@ fn read_shares() -> io::Result<(u8, Vec<(u8,Vec)>)> { let parts: Vec<_> = line.trim().split('-').collect(); if parts.len() < 3 || parts.len() > 4 { return Err(other_io_err("Share parse error: Expected 3 or 4 \ - parts searated by a minus sign", None)); + parts separated by a minus sign", None)); } let (k, n, p3) = { let mut iter = parts.into_iter(); @@ -229,83 +165,32 @@ fn read_shares() -> io::Result<(u8, Vec<(u8,Vec)>)> { Err(other_io_err("Not enough shares provided!", None)) } -fn perform_decode() -> io::Result<()> { +fn perform_decode_from_io() -> io::Result<()> { let (k, shares) = try!(read_shares()); - assert!(!shares.is_empty()); - let slen = shares[0].1.len(); - let mut col_in = Vec::with_capacity(k as usize); - let mut secret = Vec::with_capacity(slen); - for byteindex in 0 .. slen { - col_in.clear(); - for s in shares.iter().take(k as usize) { - col_in.push((s.0, s.1[byteindex])); - } - secret.push(lagrange_interpolate(&*col_in, 0u8)); - } - let mut out = io::stdout(); - try!(out.write_all(&*secret)); - out.flush() -} -fn main() { - let mut stderr = io::stderr(); - let args: Vec = 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)); - // env::set_exit_status(1); // FIXME: unstable feature - return; + return match lib::perform_decode(k, shares) { + Ok(secret) => { + let mut out = io::stdout(); + try!(out.write_all(&*secret)); + out.flush() } + Err(e) => {Err(e) as io::Result<()>} }; - - if args.len() < 2 || opt_matches.opt_present("h") { - println!( -"The program secretshare 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: secretshare [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(k, n), - Ok(Action::Decode) => perform_decode(), - Err(e) => Err(other_io_err(e, None)) - }; - - if let Err(e) = result { - drop(writeln!(&mut stderr, "{}", e)); - // env::set_exit_status(1); // FIXME: unstable feature - } +} + +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)) +} + +/// maps a ParseIntError to an io::Error +fn pie2io(p: num::ParseIntError) -> io::Error { + convert::From::from( + Error::new("Integer parsing error", Some(p.to_string())) + ) }