Finished implementing everything

This commit is contained in:
2019-02-09 01:23:04 +01:00
parent d2f5ec25ee
commit feca7d27dc
4 changed files with 173 additions and 32 deletions

View File

@@ -39,6 +39,22 @@ use std::io;
/// # Get the secret back /// # Get the secret back
/// ///
/// rssss extract <recovered-poly >recovered-secret /// 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)] #[derive(StructOpt)]
enum Cmdline { enum Cmdline {
/// Generate a poly from a secret /// 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 /// This is equivalent to solve followed by extract, but without the intermediate step
#[derive(StructOpt)] #[derive(StructOpt)]
struct Combine { 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() { fn main() {
@@ -156,8 +176,10 @@ impl Command for Solve {
.and_then(|f| rssss::s4::Share::read_from(f, self.prime)) .and_then(|f| rssss::s4::Share::read_from(f, self.prime))
.map_err(|err| Error::from(err) .map_err(|err| Error::from(err)
.context(format!("Failed to read {:?}", filename)))?) .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(()) Ok(())
} }
} }
@@ -176,7 +198,23 @@ impl Command for ExtractSecret {
impl Command for Combine { impl Command for Combine {
fn run(&self) -> Result<(), Error> { 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(())
} }
} }

View File

@@ -45,6 +45,16 @@ impl Poly {
} }
result 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 { impl Add<&Poly> for Poly {
@@ -179,4 +189,12 @@ impl Neg for Poly {
} }
self self
} }
}
#[cfg(test)]
mod test {
use super::Poly;
use num_bigint::BigUint;
use crate::primes::Prime;
} }

View File

@@ -10,7 +10,7 @@ lazy_static!{
// 2^384 - 2^128 - 2^96 + 2^32 - 1 // 2^384 - 2^128 - 2^96 + 2^32 - 1
// Chosen to be large enough to hold any AES key // Chosen to be large enough to hold any AES key
static ref PRIME_P384: BigUint = BigUint::new(vec![ static ref PRIME_P384: BigUint = BigUint::new(vec![
0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF,
0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
]); ]);
@@ -18,17 +18,36 @@ lazy_static!{
static ref PRIME_P448: BigUint = BigUint::new(vec![ static ref PRIME_P448: BigUint = BigUint::new(vec![
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE,
0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFE, 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)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub enum Prime { pub enum Prime {
// 2^384 - 2^128 - 2^96 + 2^32 - 1 // 2^384 - 2^128 - 2^96 + 2^32 - 1
P384, P384,
// 2^448 - 2^224 - 1 // 2^448 - 2^224 - 1
P448, P448,
M12,
M13,
M19,
M34,
} }
impl Prime { impl Prime {
@@ -36,6 +55,10 @@ impl Prime {
match self { match self {
Prime::P384 => 1, Prime::P384 => 1,
Prime::P448 => 2, Prime::P448 => 2,
Prime::M12 => 3,
Prime::M13 => 4,
Prime::M19 => 5,
Prime::M34 => 6,
} }
} }
@@ -43,6 +66,10 @@ impl Prime {
match self { match self {
Prime::P448 => 56, Prime::P448 => 56,
Prime::P384 => 48, Prime::P384 => 48,
Prime::M12 => 16,
Prime::M13 => 66,
Prime::M19 => 532,
Prime::M34 => 157224,
} }
} }
@@ -50,15 +77,24 @@ impl Prime {
match self { match self {
Prime::P384 => &PRIME_P384, Prime::P384 => &PRIME_P384,
Prime::P448 => &PRIME_P448, 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 { 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 { pub fn negate(self, mut value: &BigUint) -> BigUint {
self.modulus() - value 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() { match s.to_lowercase().as_str() {
"p384" => Ok(Prime::P384), "p384" => Ok(Prime::P384),
"p448" => Ok(Prime::P448), "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; use std::mem::swap;
let mut x = BigUint::zero(); let mut x = BigUint::zero();
let mut last_x = BigUint::one(); 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() { while !b.is_zero() {
let quot = &a / &b; let quot = &a / &b;
swap(&mut a, &mut b); let new_b = a % &b;
b %= &a; a = b;
swap(&mut x, &mut last_x); b = new_b;
swap(&mut y, &mut last_y);
let new_x = (last_x + prime.negate(&(&x * &quot))) % prime.modulus();
let new_y = (last_y + prime.negate(&(&y * &quot))) % prime.modulus();
last_x *= &quot; last_x = x;
x -= &last_x; last_y = y;
x = new_x;
last_y *= quot; y = new_y;
y -= &last_y;
} }
(last_x, last_y) (last_x, last_y)
} }
@@ -104,13 +144,16 @@ mod test {
use num_bigint::BigUint; use num_bigint::BigUint;
use super::Prime; 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] #[test]
fn check_value_of_p384() { fn check_value_of_p384() {
let one = &BigUint::new(vec![1]); 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() { fn check_value_of_p448() {
let one = &BigUint::new(vec![1]); 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)));
} }
} }

View File

@@ -1,10 +1,13 @@
use std::io::{self, prelude::*}; use std::io::{self, prelude::*};
use std::ops::Deref; use std::ops::Deref;
use num_bigint::BigUint; use num_bigint::BigUint;
use serde::Serialize; use serde::Serialize;
use crate::poly::Poly;
use crate::primes::Prime;
/// Structure /// Structure
/// | off | len | meaning | /// | off | len | meaning |
/// | 0 | 1 | Length of payload (L) /// | 0 | 1 | Length of payload (L)
@@ -14,7 +17,7 @@ use serde::Serialize;
/// this way, the high bit is always unset /// this way, the high bit is always unset
#[derive(Default,Clone, Debug)] #[derive(Default,Clone, Debug)]
pub struct Secret { pub struct Secret {
length: u8, length: u16,
data: Vec<u8>, data: Vec<u8>,
crc32: u32, crc32: u32,
@@ -24,12 +27,10 @@ impl Secret {
pub fn new(data: Vec<u8>) -> Self { pub fn new(data: Vec<u8>) -> Self {
if data.len() == 0 { if data.len() == 0 {
panic!("Must have at least one byte of payload"); panic!("Must have at least one byte of payload");
} else if data.len() > 127 {
panic!("Too much data");
} }
let mut res = Secret { let mut res = Secret {
length: data.len() as u8, length: data.len() as u16,
data, data,
crc32: 0, crc32: 0,
}; };
@@ -43,7 +44,7 @@ impl Secret {
use crc::crc32::{CASTAGNOLI, Digest, Hasher32}; use crc::crc32::{CASTAGNOLI, Digest, Hasher32};
let mut hasher = Digest::new(CASTAGNOLI); let mut hasher = Digest::new(CASTAGNOLI);
hasher.reset(); hasher.reset();
hasher.write(&[self.length]); hasher.write(&[1]);
hasher.write(&self.data[..]); hasher.write(&self.data[..]);
hasher.write(&[0, 0, 0, 0]); hasher.write(&[0, 0, 0, 0]);
hasher.sum32() hasher.sum32()
@@ -68,10 +69,14 @@ impl From<BigUint> for Secret {
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
let decoded = ui.to_bytes_be(); let decoded = ui.to_bytes_be();
let mut res = Secret::default(); let mut res = Secret::default();
res.length = decoded[0]; if decoded[0] != 1 {
let len = res.length as usize; panic!("Unknown version {}", decoded[0]);
res.data = decoded[1..len+1].to_owned(); }
res.crc32 = BigEndian::read_u32(&decoded[len+1..len+5]); 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 res
} }
@@ -82,7 +87,7 @@ impl From<Secret> for BigUint {
use std::io::Write; use std::io::Write;
use byteorder::{WriteBytesExt, BigEndian}; use byteorder::{WriteBytesExt, BigEndian};
let mut buf = vec![]; let mut buf = vec![];
buf.write(&[obj.length]); buf.write(&[1]);
buf.write(&obj.data[..]); buf.write(&obj.data[..]);
buf.write_u32::<BigEndian>(obj.crc32); buf.write_u32::<BigEndian>(obj.crc32);
BigUint::from_bytes_be(&buf[..]) BigUint::from_bytes_be(&buf[..])
@@ -135,4 +140,41 @@ impl Share {
Ok(Share{ prime, x, y }) 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()
} }