Finished implementing everything
This commit is contained in:
42
src/main.rs
42
src/main.rs
@@ -39,6 +39,22 @@ use std::io;
|
||||
/// # Get the secret back
|
||||
///
|
||||
/// rssss extract <recovered-poly >recovered-secret
|
||||
///
|
||||
/// Choose your prime based on the size of data to be stored and your desired security level
|
||||
///
|
||||
/// Implemented primes:
|
||||
///
|
||||
/// M12: (127 bits) 2^127-1
|
||||
/// P384: (384 bits) 2^384-2^128-2^96+2^32-1
|
||||
/// P448: (448 bits) 2^448-2^224-1
|
||||
/// M13: (521 bits) 2^521-1
|
||||
/// M19: (4253 bits) 2^4253-1
|
||||
/// M34: (~1.2MBit) 2^1257787-1
|
||||
///
|
||||
/// M19 should only be used for *extremely* secure sharing of RSA keys; M34
|
||||
/// probably shouldn't be used at all. It will likely be extremely slow;
|
||||
/// you should rather generate shares of an AES key and use that to encrypt your real secret
|
||||
|
||||
#[derive(StructOpt)]
|
||||
enum Cmdline {
|
||||
/// Generate a poly from a secret
|
||||
@@ -93,6 +109,10 @@ struct ExtractSecret {
|
||||
/// This is equivalent to solve followed by extract, but without the intermediate step
|
||||
#[derive(StructOpt)]
|
||||
struct Combine {
|
||||
#[structopt(short="p", long="prime", default_value="p384", parse(try_from_str))]
|
||||
prime: rssss::primes::Prime,
|
||||
#[structopt(parse(from_os_str))]
|
||||
data: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -156,8 +176,10 @@ impl Command for Solve {
|
||||
.and_then(|f| rssss::s4::Share::read_from(f, self.prime))
|
||||
.map_err(|err| Error::from(err)
|
||||
.context(format!("Failed to read {:?}", filename)))?)
|
||||
}).collect::<Result<Vec<rssss::s4::Share>, Error>>();
|
||||
}).collect::<Result<Vec<rssss::s4::Share>, Error>>()?;
|
||||
|
||||
let poly = rssss::s4::solve(&shares[..]);
|
||||
serde_cbor::to_writer(&mut io::stdout(), &poly)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -176,7 +198,23 @@ impl Command for ExtractSecret {
|
||||
|
||||
impl Command for Combine {
|
||||
fn run(&self) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
let shares = self.data.iter().map(|filename| {
|
||||
Ok(File::open(&filename)
|
||||
.and_then(|f| rssss::s4::Share::read_from(f, self.prime))
|
||||
.map_err(|err| Error::from(err)
|
||||
.context(format!("Failed to read {:?}", filename)))?)
|
||||
}).collect::<Result<Vec<rssss::s4::Share>, Error>>()?;
|
||||
|
||||
let secret = rssss::s4::Secret::from(rssss::s4::interpolate0(&shares[..]));
|
||||
|
||||
if !secret.check_crc() {
|
||||
eprintln!("CRC failed; not enough shares or corrupted share")
|
||||
}
|
||||
|
||||
io::stdout().write_all(&secret[..])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
src/poly.rs
18
src/poly.rs
@@ -45,6 +45,16 @@ impl Poly {
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn trim(&mut self) {
|
||||
while !self.coeff.is_empty() {
|
||||
if self.coeff[self.coeff.len()-1].is_zero() {
|
||||
self.coeff.pop();
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Poly> for Poly {
|
||||
@@ -179,4 +189,12 @@ impl Neg for Poly {
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Poly;
|
||||
use num_bigint::BigUint;
|
||||
use crate::primes::Prime;
|
||||
|
||||
}
|
||||
@@ -10,7 +10,7 @@ lazy_static!{
|
||||
// 2^384 - 2^128 - 2^96 + 2^32 - 1
|
||||
// Chosen to be large enough to hold any AES key
|
||||
static ref PRIME_P384: BigUint = BigUint::new(vec![
|
||||
0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFE,
|
||||
0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF,
|
||||
0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
]);
|
||||
@@ -18,17 +18,36 @@ lazy_static!{
|
||||
static ref PRIME_P448: BigUint = BigUint::new(vec![
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE,
|
||||
0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF,
|
||||
0xFFFFFFFE, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
]);
|
||||
|
||||
static ref PRIME_M12: BigUint = BigUint::new(vec![
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x7FFFFFFF,
|
||||
]);
|
||||
|
||||
static ref PRIME_M13: BigUint = (BigUint::one() << 521) - BigUint::one();
|
||||
static ref PRIME_M19: BigUint = (BigUint::one() << 4253) - BigUint::one();
|
||||
static ref PRIME_M34: BigUint = (BigUint::one() << 1257787) - BigUint::one();
|
||||
}
|
||||
|
||||
// Other primes to possibly implemet:
|
||||
// 2^521-1
|
||||
// 2^384 - 2^128 - 2^96 + 2^32 - 1 (still a bit painful)
|
||||
// 2^448 - 2^224 - 1
|
||||
// 2^256 - 2^32 - 977
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum Prime {
|
||||
// 2^384 - 2^128 - 2^96 + 2^32 - 1
|
||||
P384,
|
||||
// 2^448 - 2^224 - 1
|
||||
P448,
|
||||
M12,
|
||||
M13,
|
||||
M19,
|
||||
M34,
|
||||
}
|
||||
|
||||
impl Prime {
|
||||
@@ -36,6 +55,10 @@ impl Prime {
|
||||
match self {
|
||||
Prime::P384 => 1,
|
||||
Prime::P448 => 2,
|
||||
Prime::M12 => 3,
|
||||
Prime::M13 => 4,
|
||||
Prime::M19 => 5,
|
||||
Prime::M34 => 6,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +66,10 @@ impl Prime {
|
||||
match self {
|
||||
Prime::P448 => 56,
|
||||
Prime::P384 => 48,
|
||||
Prime::M12 => 16,
|
||||
Prime::M13 => 66,
|
||||
Prime::M19 => 532,
|
||||
Prime::M34 => 157224,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,15 +77,24 @@ impl Prime {
|
||||
match self {
|
||||
Prime::P384 => &PRIME_P384,
|
||||
Prime::P448 => &PRIME_P448,
|
||||
Prime::M12 => &PRIME_M12,
|
||||
Prime::M13 => &PRIME_M13,
|
||||
Prime::M19 => &PRIME_M19,
|
||||
Prime::M34 => &PRIME_M34,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inverse(self, value: BigUint) -> BigUint {
|
||||
ext_gcd(value, self.modulus().clone()).0
|
||||
let saved_value = value.clone();
|
||||
let a = ext_gcd(self, value, self.modulus().clone()).0;
|
||||
assert_eq!((&a * saved_value) % self.modulus(), BigUint::one());
|
||||
a
|
||||
}
|
||||
|
||||
pub fn negate(self, value: &BigUint) -> BigUint {
|
||||
self.modulus() - value
|
||||
pub fn negate(self, mut value: &BigUint) -> BigUint {
|
||||
let result = self.modulus() - (value % self.modulus());
|
||||
assert_eq!(BigUint::zero(), (&result + value) % self.modulus());
|
||||
result
|
||||
}
|
||||
|
||||
}
|
||||
@@ -70,12 +106,16 @@ impl FromStr for Prime {
|
||||
match s.to_lowercase().as_str() {
|
||||
"p384" => Ok(Prime::P384),
|
||||
"p448" => Ok(Prime::P448),
|
||||
_ => Err(failure::err_msg("Invalid prime spec; expected P384 or P448")),
|
||||
"m12" => Ok(Prime::M12),
|
||||
"m13" => Ok(Prime::M13),
|
||||
"m19" => Ok(Prime::M19),
|
||||
"m34" => Ok(Prime::M34),
|
||||
_ => Err(failure::err_msg("Invalid prime spec; expected P384, P448, M12, M13, M19, or M34")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ext_gcd(mut a: BigUint, mut b: BigUint) -> (BigUint, BigUint) {
|
||||
fn ext_gcd(prime: Prime, mut a: BigUint, mut b: BigUint) -> (BigUint, BigUint) {
|
||||
use std::mem::swap;
|
||||
let mut x = BigUint::zero();
|
||||
let mut last_x = BigUint::one();
|
||||
@@ -84,17 +124,17 @@ fn ext_gcd(mut a: BigUint, mut b: BigUint) -> (BigUint, BigUint) {
|
||||
|
||||
while !b.is_zero() {
|
||||
let quot = &a / &b;
|
||||
swap(&mut a, &mut b);
|
||||
b %= &a;
|
||||
swap(&mut x, &mut last_x);
|
||||
swap(&mut y, &mut last_y);
|
||||
let new_b = a % &b;
|
||||
a = b;
|
||||
b = new_b;
|
||||
|
||||
let new_x = (last_x + prime.negate(&(&x * "))) % prime.modulus();
|
||||
let new_y = (last_y + prime.negate(&(&y * "))) % prime.modulus();
|
||||
|
||||
last_x *= "
|
||||
x -= &last_x;
|
||||
|
||||
last_y *= quot;
|
||||
y -= &last_y;
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
x = new_x;
|
||||
y = new_y;
|
||||
}
|
||||
(last_x, last_y)
|
||||
}
|
||||
@@ -104,13 +144,16 @@ mod test {
|
||||
use num_bigint::BigUint;
|
||||
use super::Prime;
|
||||
|
||||
// These definitions are much slower than the above static ones, but in
|
||||
// exchange, they're much more likely to be correct
|
||||
#[test]
|
||||
fn check_value_of_p384() {
|
||||
let one = &BigUint::new(vec![1]);
|
||||
assert_eq!(Prime::P384::modulus, one << 384 - one - one << 128 - one << 96 - one << 32);
|
||||
assert_eq!(Prime::P384.modulus(), &((one << 384) - one - (one << 128) - (one << 96) + (one << 32)));
|
||||
}
|
||||
#[test]
|
||||
fn check_value_of_p448() {
|
||||
let one = &BigUint::new(vec![1]);
|
||||
assert_eq!(Prime::P448::modulus, one << 448 - one - one << 224);
|
||||
assert_eq!(Prime::P448.modulus(), &((one << 448) - one - (one << 224)));
|
||||
}
|
||||
}
|
||||
64
src/s4.rs
64
src/s4.rs
@@ -1,10 +1,13 @@
|
||||
use std::io::{self, prelude::*};
|
||||
use std::ops::Deref;
|
||||
|
||||
|
||||
use num_bigint::BigUint;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::poly::Poly;
|
||||
use crate::primes::Prime;
|
||||
|
||||
|
||||
/// Structure
|
||||
/// | off | len | meaning |
|
||||
/// | 0 | 1 | Length of payload (L)
|
||||
@@ -14,7 +17,7 @@ use serde::Serialize;
|
||||
/// this way, the high bit is always unset
|
||||
#[derive(Default,Clone, Debug)]
|
||||
pub struct Secret {
|
||||
length: u8,
|
||||
length: u16,
|
||||
data: Vec<u8>,
|
||||
crc32: u32,
|
||||
|
||||
@@ -24,12 +27,10 @@ impl Secret {
|
||||
pub fn new(data: Vec<u8>) -> Self {
|
||||
if data.len() == 0 {
|
||||
panic!("Must have at least one byte of payload");
|
||||
} else if data.len() > 127 {
|
||||
panic!("Too much data");
|
||||
}
|
||||
|
||||
let mut res = Secret {
|
||||
length: data.len() as u8,
|
||||
length: data.len() as u16,
|
||||
data,
|
||||
crc32: 0,
|
||||
};
|
||||
@@ -43,7 +44,7 @@ impl Secret {
|
||||
use crc::crc32::{CASTAGNOLI, Digest, Hasher32};
|
||||
let mut hasher = Digest::new(CASTAGNOLI);
|
||||
hasher.reset();
|
||||
hasher.write(&[self.length]);
|
||||
hasher.write(&[1]);
|
||||
hasher.write(&self.data[..]);
|
||||
hasher.write(&[0, 0, 0, 0]);
|
||||
hasher.sum32()
|
||||
@@ -68,10 +69,14 @@ impl From<BigUint> for Secret {
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
let decoded = ui.to_bytes_be();
|
||||
let mut res = Secret::default();
|
||||
res.length = decoded[0];
|
||||
let len = res.length as usize;
|
||||
res.data = decoded[1..len+1].to_owned();
|
||||
res.crc32 = BigEndian::read_u32(&decoded[len+1..len+5]);
|
||||
if decoded[0] != 1 {
|
||||
panic!("Unknown version {}", decoded[0]);
|
||||
}
|
||||
if decoded.len() < 5 {
|
||||
panic!("Data too short");
|
||||
}
|
||||
res.data = decoded[1..decoded.len() - 4].to_owned();
|
||||
res.crc32 = BigEndian::read_u32(&decoded[decoded.len()-4 .. ]);
|
||||
|
||||
res
|
||||
}
|
||||
@@ -82,7 +87,7 @@ impl From<Secret> for BigUint {
|
||||
use std::io::Write;
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
let mut buf = vec![];
|
||||
buf.write(&[obj.length]);
|
||||
buf.write(&[1]);
|
||||
buf.write(&obj.data[..]);
|
||||
buf.write_u32::<BigEndian>(obj.crc32);
|
||||
BigUint::from_bytes_be(&buf[..])
|
||||
@@ -135,4 +140,41 @@ impl Share {
|
||||
|
||||
Ok(Share{ prime, x, y })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solve(shares: &[Share]) -> Poly {
|
||||
let prime = shares[0].prime;
|
||||
let mut result = Poly::zero(prime);
|
||||
for j in 0..shares.len() {
|
||||
let mut basis = Poly::zero(prime) + &shares[j].y;
|
||||
for m in 0..shares.len() {
|
||||
if j == m {
|
||||
continue;
|
||||
}
|
||||
basis = (Poly::x(prime) + prime.negate(&shares[m].x))
|
||||
/ (&shares[j].x + prime.negate(&shares[m].x)) * &basis;
|
||||
}
|
||||
result += &basis;
|
||||
}
|
||||
result.trim();
|
||||
result
|
||||
}
|
||||
|
||||
pub fn interpolate0(shares: &[Share]) -> BigUint {
|
||||
use num_traits::Zero;
|
||||
let prime = shares[0].prime;
|
||||
let mut result = BigUint::zero();
|
||||
for j in 0..shares.len() {
|
||||
let mut basis = shares[j].y.clone();
|
||||
for m in 0..shares.len() {
|
||||
if j == m {
|
||||
continue;
|
||||
}
|
||||
basis *= &shares[m].x *
|
||||
prime.inverse(&shares[m].x + prime.negate(&shares[j].x));
|
||||
basis %= prime.modulus();
|
||||
}
|
||||
result += basis;
|
||||
}
|
||||
result % prime.modulus()
|
||||
}
|
||||
Reference in New Issue
Block a user