From 1b74e60eff93f90adb767da0b3c3d7eb6697ce7d Mon Sep 17 00:00:00 2001 From: TQ Hirsch Date: Sun, 14 Jan 2024 03:50:39 +0100 Subject: [PATCH] Massive rewrite to be based on GF(2^8) rather than a prime field; this results in much faster operation and no size limit --- Cargo.toml | 9 +- src/gf.rs | 31 +++++++ src/gf256.rs | 188 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +- src/main.rs | 69 ++++++++-------- src/poly.rs | 224 ++++++++++++++++++++++++++++---------------------- src/primes.rs | 159 ----------------------------------- src/s4.rs | 208 ++++++++++++++++++++++++++-------------------- 8 files changed, 513 insertions(+), 380 deletions(-) create mode 100644 src/gf.rs create mode 100644 src/gf256.rs delete mode 100644 src/primes.rs diff --git a/Cargo.toml b/Cargo.toml index c83c5f6..afe29b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,10 +2,9 @@ name = "rssss" version = "0.1.0" authors = ["TQ Hirsch "] -edition = "2018" +edition = "2021" [dependencies] -num-bigint = {version = "0.2.2", features = ["rand", "serde"]} lazy_static = "1.2.0" structopt = "0.2.14" failure = "0.1.5" @@ -15,4 +14,8 @@ num-traits = "0.2.6" rand = "0.6.5" serde = "1.0.87" serde_derive = "1.0.87" -serde_cbor = "0.9.0" \ No newline at end of file +serde_cbor = "0.9.0" +serde_json = "1.0.111" +bendy = "0.3.3" +generic-array = "1.0.0" +anyhow = "1.0.79" \ No newline at end of file diff --git a/src/gf.rs b/src/gf.rs new file mode 100644 index 0000000..24dbab7 --- /dev/null +++ b/src/gf.rs @@ -0,0 +1,31 @@ +use std::ops::{Add, Mul, Sub}; +use generic_array::{ArrayLength, GenericArray}; +use rand::{Rng, RngCore}; +use generic_array::typenum::Unsigned; + +pub trait GF: Add + Sub + Mul + Copy + Clone + Eq + TryFrom { + const ORDER: usize; + type ChunkSize: ArrayLength; + + + /// Additive identity + const ZERO: Self; + /// Multiplicative identity + const ONE: Self; + + /// Multiplicative inverse + fn minv(self) -> Self; + + fn decode(chunk: GenericArray) -> Self; + fn encode(self) -> GenericArray; + + fn decode_slice(s: &[u8]) -> Self { + let mut chunk = GenericArray::::default(); + for i in 0..Self::ChunkSize::to_usize() { + chunk[i] = s.get(i).copied().unwrap_or(0); + } + Self::decode(chunk) + } + fn sample(rng: &mut (impl Rng + ?Sized)) -> Self; +} + diff --git a/src/gf256.rs b/src/gf256.rs new file mode 100644 index 0000000..56e1965 --- /dev/null +++ b/src/gf256.rs @@ -0,0 +1,188 @@ +use std::error::Error; +use std::fmt::{Debug, Display, Formatter}; +use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign}; +use generic_array::GenericArray; +use generic_array::typenum::U1; +use rand::distributions::{Standard}; +use rand::prelude::Distribution; +use rand::Rng; +use serde_derive::{Deserialize, Serialize}; +use crate::gf::GF; + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +/// Field element of GF(2^8) +/// +/// Multiplication is performed modulo $x^8 + x^4 + x^3 + x + 1$, or `{11B}` +/// +#[derive(Serialize, Deserialize)] +pub struct GF256(u8); + +impl GF256 { + + /// Multiply by x, reducing modulo 14E + const fn mulx(self) -> Self { + let poly = self.0; + let mask = ((poly as i8) >> 7) as u8; + GF256(poly << 1 ^ (0x1B & mask)) + } + +} + +impl GF for GF256 { + const ORDER: usize = 256; + type ChunkSize = U1; + const ZERO: Self = GF256(0); + const ONE: Self = GF256(1); + + + /// Multiplicative inverse + /// Note that this is an expensive operation + fn minv(self) -> Self { + // For constant time evaluation, we compute self**254 + let mut res = self; + let mut factor = 1; + for _ in 0..6 { + res = res * res * self; + factor = factor * 2 + 1; + } + res = res * res; + factor = factor*2; + assert_eq!(factor, 254); + + res + } + + fn decode(chunk: GenericArray) -> Self { + GF256(u8::from_le_bytes(chunk.into_array())) + } + + fn encode(self) -> GenericArray { + self.0.to_le_bytes().into() + } + + fn sample(rng: &mut (impl Rng + ?Sized)) -> Self { + GF256(rng.sample(Standard)) + } +} + +impl From for GF256 { + fn from(value: u8) -> Self { + GF256(value) + } +} + +impl From for u8 { + fn from(value: GF256) -> Self { + value.0 + } +} + +impl Add for GF256 { + type Output = GF256; + + fn add(self, rhs: Self) -> Self::Output { + GF256(self.0 ^ rhs.0) + } +} + +impl AddAssign for GF256 { + fn add_assign(&mut self, rhs: Self) { + self.0 ^= rhs.0; + } +} + +// Note that in GF(2^n), addition is equivalent to subtraction +impl Sub for GF256 { + type Output = GF256; + + fn sub(self, rhs: Self) -> Self::Output { + self + rhs + } +} + +impl SubAssign for GF256 { + fn sub_assign(&mut self, rhs: Self) { + *self += rhs + } +} + + +impl Mul for GF256 { + type Output = GF256; + + fn mul(self, rhs: Self) -> Self::Output { + let mut res = 0; + let mut a = self.0; + let mut b = rhs; + // println!("{:2} {:2} {:2}", "a", "b", "m"); + for _ in 0..8 { + let mask = uneg(a & 1); + // println!("{:02x} {:02x} {:02x}", a, b.0, mask); + res ^= b.0 & mask; + a >>= 1; + b = b.mulx(); + } + GF256(res) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct RangeError; + +impl Display for RangeError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "Range error") + } +} + +impl Error for RangeError { +} + +impl TryFrom for GF256 { + type Error = RangeError; + + fn try_from(value: usize) -> Result { + if value < Self::ORDER { + Ok(GF256(value as u8)) + } else { + Err(RangeError) + } + } +} + + +// Utility functions +/// Unsigned negate +fn uneg(v: u8) -> u8 { + (-(v as i8)) as u8 +} + +#[cfg(test)] +mod test { + use super::GF256; + use crate::gf::GF; + + #[test] + fn mulx_1() { + assert_eq!(GF256(1).mulx(), GF256(2)) + } + + #[test] + fn mul_1_1() { + assert_eq!(GF256(1) * GF256(1), GF256(1)) + } + + + #[test] + fn minv_works() { + // 0 has no multiplicative inverse + for i in 1..=255 { + let base = GF256(i); + let inv = base.minv(); + println!("Testing {{{i:02x}}}^-1 = {{{:02x}}}", inv.0); + assert_eq!(GF256(1), base*inv); + } + } +} + diff --git a/src/lib.rs b/src/lib.rs index 3d6ae2b..281a1bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ -use num_bigint::BigUint; -use lazy_static::lazy_static; +pub mod gf; +pub mod gf256; -pub mod primes; pub mod poly; pub mod s4; diff --git a/src/main.rs b/src/main.rs index 0c846fb..4047c85 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,11 @@ use structopt::StructOpt; use std::path::PathBuf; use failure::Error; use std::io; - +use generic_array::typenum::Unsigned; +use rand::Rng; +use rssss::gf256::GF256; +use rssss::gf::GF; +use rssss::poly::UniformPoly; /// Rust implementation of Shamir Secret Sharing @@ -80,20 +84,17 @@ enum Cmdline { #[derive(StructOpt)] struct GenPoly { - #[structopt(short="p", long="prime", default_value="p384", parse(try_from_str))] - prime: rssss::primes::Prime, - min_shares: u32, } #[derive(StructOpt)] struct GenShare { + /// The share number. Must be between 1 and 255 + share_no: u32, } #[derive(StructOpt)] struct Solve { - #[structopt(short="p", long="prime", default_value="p384", parse(try_from_str))] - prime: rssss::primes::Prime, #[structopt(parse(from_os_str))] data: Vec, } @@ -101,23 +102,19 @@ struct Solve { /// Extract a secret #[derive(StructOpt)] struct ExtractSecret { - #[structopt(short="p", long="prime", default_value="p384", parse(try_from_str))] - prime: rssss::primes::Prime, } /// Combine a set of shares to find the secret /// 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, } fn main() { let cmdline = Cmdline::from_args(); - let cmd: &Command = match cmdline { + let cmd: &dyn Command = match cmdline { Cmdline::GenPoly(ref cmd) => cmd, Cmdline::GenShare(ref cmd) => cmd, Cmdline::Solve(ref cmd) => cmd, @@ -125,7 +122,7 @@ fn main() { Cmdline::Combine(ref cmd) => cmd, }; - cmd.run(); + cmd.run().unwrap(); } trait Command { @@ -135,7 +132,6 @@ trait Command { impl Command for GenPoly { fn run(&self) -> Result<(), Error> { use std::io::Read; - use num_bigint::{RandBigInt, BigUint}; let mut rng = rand::rngs::JitterRng::new()?; @@ -143,26 +139,30 @@ impl Command for GenPoly { io::stdin().read_to_end(&mut payload)?; let secret = rssss::s4::Secret::new(payload); - let mut poly = rssss::poly::Poly::zero(self.prime); - for i in 1..self.min_shares { - poly += rng.gen_biguint_below(self.prime.modulus()); - poly.mul_x(); + let mut payload = Vec::new(); + secret.to_buf(&mut payload); + + + let mut polys = Vec::with_capacity(payload.len()); + for i in 0..payload.len() { + polys.push(rng.sample(UniformPoly{ intercept: GF256::from(payload[i]), degree: self.min_shares as usize})); } - poly += &BigUint::from(secret); // export the poly - serde_cbor::to_writer(&mut io::stdout(), &poly)?; - + serde_cbor::to_writer(&mut io::stdout(), &polys)?; Ok(()) } } impl Command for GenShare { fn run(&self) -> Result<(), Error> { - let mut poly: rssss::poly::Poly = serde_cbor::from_reader(io::stdin())?; + eprintln!("Reading poly"); + let mut poly: Vec> = serde_cbor::from_reader(io::stdin())?; + eprintln!("Read poly"); let mut rng = rand::rngs::JitterRng::new()?; - let share = rssss::s4::Share::new(&mut rng, &poly); + let share = rssss::s4::Share::new((self.share_no as u8).into(), poly.as_slice()); + eprintln!("{share:?}"); share.write_to(io::stdout())?; Ok(()) } @@ -173,10 +173,10 @@ impl Command for Solve { use std::fs::File; let shares = self.data.iter().map(|filename| { Ok(File::open(&filename) - .and_then(|f| rssss::s4::Share::read_from(f, self.prime)) + .and_then(|f| rssss::s4::Share::read_from(f)) .map_err(|err| Error::from(err) .context(format!("Failed to read {:?}", filename)))?) - }).collect::, Error>>()?; + }).collect::>, Error>>()?; let poly = rssss::s4::solve(&shares[..]); serde_cbor::to_writer(&mut io::stdout(), &poly)?; @@ -186,11 +186,12 @@ impl Command for Solve { impl Command for ExtractSecret { fn run(&self) -> Result<(), Error> { - use num_bigint::BigUint; - use num_traits::Zero; use std::io::Write; - let mut poly: rssss::poly::Poly = serde_cbor::from_reader(io::stdin())?; - let secret = rssss::s4::Secret::from(poly.eval_at(&BigUint::zero())); + let mut poly: Vec> = serde_cbor::from_reader(io::stdin())?; + let interpolated = poly.into_iter().map(|p| p.eval_at(GF256::ZERO)) + .flat_map(|f| f.encode()) + .collect::>(); + let secret = rssss::s4::Secret::from_buf(interpolated.as_slice())?; io::stdout().write_all(&secret[..])?; Ok(()) } @@ -202,12 +203,16 @@ impl Command for Combine { 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)) + .and_then(|f| rssss::s4::Share::read_from(f)) .map_err(|err| Error::from(err) .context(format!("Failed to read {:?}", filename)))?) - }).collect::, Error>>()?; - - let secret = rssss::s4::Secret::from(rssss::s4::interpolate0(&shares[..])); + }).collect::>, Error>>()?; + let mut interpolated = rssss::s4::interpolate0(&shares[..]); + let mut secret = Vec::with_capacity(interpolated.len() * ::ChunkSize::to_usize()); + for chunk in interpolated { + secret.extend_from_slice(&chunk.encode()[..]) + } + let secret = rssss::s4::Secret::from_buf(secret.as_slice())?; if !secret.check_crc() { eprintln!("CRC failed; not enough shares or corrupted share") diff --git a/src/poly.rs b/src/poly.rs index c70f78f..3dbaeac 100644 --- a/src/poly.rs +++ b/src/poly.rs @@ -1,54 +1,56 @@ +use std::fmt::Formatter; use std::io::{self, prelude::*}; -use std::ops::{Mul, Div, Add, AddAssign, DivAssign, Neg}; +use std::marker::PhantomData; +use std::ops::{Mul, Div, Add, AddAssign, DivAssign, Neg, Sub}; -use num_bigint::BigUint; use num_traits::{Zero, One}; +use rand::distributions::Standard; +use rand::prelude::Distribution; +use rand::Rng; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde::de::{SeqAccess, Visitor}; +use serde::ser::SerializeSeq; use serde_derive::{Serialize, Deserialize}; - -use crate::primes::Prime; +use crate::gf::GF; -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Poly { + +#[derive(Clone, Debug)] +pub struct Poly { // coeff[0] + coeff[1]*x + coeff[2] * x^2 // i.e., $$\sum_i coeff_i x^i$$ - pub prime: Prime, - coeff: Vec, + coeff: Vec, } -impl Poly { - pub fn zero(modulus: Prime) -> Self { +impl Poly { + pub fn zero() -> Self { Poly { - prime: modulus, coeff: vec![], } } - pub fn x(modulus: Prime) -> Self { + pub fn x() -> Self { Poly { - prime: modulus, - coeff: vec![BigUint::zero(), BigUint::one()], + coeff: vec![F::ZERO, F::ONE], } } pub fn mul_x(&mut self) { - self.coeff.insert(0, BigUint::zero()); + self.coeff.insert(0, F::ZERO); } - pub fn eval_at(&self, point: &BigUint) -> BigUint { - let mut result: BigUint = BigUint::zero(); - for item in self.coeff.iter().rev() { - result *= point; - result += item; - result %= self.prime.modulus(); + pub fn eval_at(&self, point: F) -> F { + let mut result: F = F::ZERO; + for item in self.coeff.iter().rev().copied() { + result = result * point + item; } result } pub fn trim(&mut self) { while !self.coeff.is_empty() { - if self.coeff[self.coeff.len()-1].is_zero() { + if self.coeff[self.coeff.len()-1] == F::ZERO { self.coeff.pop(); } else { break @@ -57,144 +59,174 @@ impl Poly { } } -impl Add<&Poly> for Poly { - type Output = Poly; +impl Add<&Poly> for Poly { + type Output = Poly; - fn add(self, rhs: &Poly) -> Self::Output { + fn add(self, rhs: &Poly) -> Self::Output { let mut res = self.clone(); res += rhs; res } } -impl AddAssign<&Poly> for Poly { - fn add_assign(&mut self, rhs: &Poly) { - assert_eq!(self.prime, rhs.prime); +impl AddAssign<&Poly> for Poly { + fn add_assign(&mut self, rhs: &Poly) { if self.coeff.len() < rhs.coeff.len() { - self.coeff.extend(::std::iter::repeat(BigUint::zero()).take(rhs.coeff.len() - self.coeff.len())); + self.coeff.extend(::std::iter::repeat(F::ZERO).take(rhs.coeff.len() - self.coeff.len())); } - for (a, b) in self.coeff.iter_mut().zip(rhs.coeff.iter()) { - *a += b; - *a %= self.prime.modulus(); + for (a, b) in self.coeff.iter_mut().zip(rhs.coeff.iter().copied()) { + *a = *a + b; } } } -impl Add<&BigUint> for Poly { - type Output = Poly; +impl Add for Poly { + type Output = Poly; - fn add(mut self, rhs: &BigUint) -> Self::Output { + fn add(mut self, rhs: Poly) -> Self::Output { + self += &rhs; + self + } +} + +impl Add for Poly { + type Output = Poly; + fn add(mut self, rhs: F) -> Self::Output { self += rhs; self } } -impl AddAssign<&BigUint> for Poly { - fn add_assign(&mut self, rhs: &BigUint) { + +impl AddAssign for Poly { + fn add_assign(&mut self, rhs: F) { if self.coeff.len() == 0 { - self.coeff.push(rhs % self.prime.modulus()) + self.coeff.push(rhs) } else { - self.coeff[0] += rhs; - self.coeff[0] %= self.prime.modulus() + self.coeff[0] = self.coeff[0] + rhs; } } } -impl Add for Poly { - type Output = Poly; +impl Sub for Poly { + type Output = Self; - fn add(mut self, rhs: BigUint) -> Self::Output { - self += rhs; - self + fn sub(mut self, rhs: F) -> Self::Output { + self + rhs } } -impl AddAssign for Poly { - fn add_assign(&mut self, rhs: BigUint) { - if self.coeff.len() == 0 { - self.coeff.push(rhs % self.prime.modulus()) - } else { - self.coeff[0] += rhs; - self.coeff[0] %= self.prime.modulus() - } - } -} - -impl Mul<&BigUint> for Poly { - type Output = Poly; - fn mul(mut self, rhs: &BigUint) -> Poly { +impl Mul for Poly { + type Output = Poly; + fn mul(mut self, rhs: F) -> Poly { for a in self.coeff.iter_mut() { - *a *= rhs; - *a %= self.prime.modulus(); + *a = *a * rhs; } self } } -impl Mul<&BigUint> for &Poly { - type Output = Poly; - fn mul(mut self, rhs: &BigUint) -> Poly { - Poly { - prime: self.prime, - coeff: self.coeff.iter().map(|a| (a * rhs) % self.prime.modulus()).collect(), - } - } -} -impl Mul<&Poly> for Poly { - type Output = Poly; +impl Mul<&Poly> for Poly { + type Output = Poly; - fn mul(mut self, rhs: &Poly) -> Self::Output { - let mut result = Poly::zero(self.prime); - for factor in rhs.coeff.iter() { - result += &(&self * factor); + fn mul(mut self, rhs: &Poly) -> Self::Output { + let mut result = Self::zero(); + for factor in rhs.coeff.iter().copied() { + result += &(self.clone() * factor); self.mul_x(); } result } } -impl Mul<&Poly> for &Poly { - type Output = Poly; +impl Mul<&Poly> for &Poly { + type Output = Poly; - fn mul(self, rhs: &Poly) -> Self::Output { + fn mul(self, rhs: &Poly) -> Self::Output { self.clone() * rhs } } -impl Div for Poly { - type Output = Poly; +impl Div for Poly { + type Output = Poly; - fn div(mut self, rhs: BigUint) -> Poly { + fn div(mut self, rhs: F) -> Poly { self /= rhs; self } } -impl DivAssign for Poly { - fn div_assign(&mut self, rhs: BigUint) { - let inv = self.prime.inverse(rhs); +impl DivAssign for Poly { + fn div_assign(&mut self, rhs: F) { + let inv = rhs.minv(); for a in self.coeff.iter_mut() { - *a *= &inv; + *a = *a * inv; } } } -impl Neg for Poly { - type Output = Poly; +impl Neg for Poly { + type Output = Poly; fn neg(mut self) -> Self::Output { - for a in self.coeff.iter_mut() { - *a = self.prime.negate(&*a); - } + // Elements in GF(2^n) are their own negative self } } -#[cfg(test)] -mod test { - use super::Poly; - use num_bigint::BigUint; - use crate::primes::Prime; +pub struct UniformPoly{ + pub intercept: F, + pub degree: usize +} +impl Distribution> for UniformPoly { + fn sample(&self, rng: &mut R) -> Poly { + let mut coeff = Vec::with_capacity(self.degree + 1); + coeff.push(self.intercept); + for _ in 1..=self.degree { + coeff.push(F::sample(rng)); + } + while coeff[self.degree] == F::ZERO { + // highest degree coefficient must be non-zero + coeff[self.degree] = F::sample(rng); + } + + Poly{ + coeff, + } + } +} + +impl Serialize for Poly { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + let mut seq = serializer.serialize_seq(Some(self.coeff.len()))?; + for coeff in self.coeff.iter() { + seq.serialize_element(coeff)? + }; + seq.end() + } +} + +impl <'de, F:GF+Deserialize<'de>+'de> Deserialize<'de> for Poly { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { + struct V<'de, F: GF+Deserialize<'de>+'de>(PhantomData<&'de F>); + impl<'de, F: GF+Deserialize<'de>> Visitor<'de> for V<'de, F> { + type Value = Poly; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + formatter.write_str("list of coefficients") + } + + fn visit_seq(self, mut seq: A) -> Result where A: SeqAccess<'de> { + let mut coeff = Vec::with_capacity(seq.size_hint().unwrap_or(4)); + while let Some(item) = seq.next_element()? { + coeff.push(item) + } + Ok(Poly{ coeff }) + } + } + + deserializer.deserialize_seq(V(PhantomData)) + } } \ No newline at end of file diff --git a/src/primes.rs b/src/primes.rs deleted file mode 100644 index e6e74a8..0000000 --- a/src/primes.rs +++ /dev/null @@ -1,159 +0,0 @@ -use std::str::FromStr; - -use num_bigint::BigUint; -use lazy_static::lazy_static; -use num_traits::identities::{Zero, One}; - -use serde_derive::{Serialize, Deserialize}; - -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, 0x00000000, 0x00000000, 0xFFFFFFFF, - 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - ]); - - static ref PRIME_P448: BigUint = BigUint::new(vec![ - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, - 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 { - pub fn to_id(self) -> u8 { - match self { - Prime::P384 => 1, - Prime::P448 => 2, - Prime::M12 => 3, - Prime::M13 => 4, - Prime::M19 => 5, - Prime::M34 => 6, - } - } - - pub fn byte_size(self) -> usize { - match self { - Prime::P448 => 56, - Prime::P384 => 48, - Prime::M12 => 16, - Prime::M13 => 66, - Prime::M19 => 532, - Prime::M34 => 157224, - } - } - - pub fn modulus(self) -> &'static BigUint { - 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 { - 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, mut value: &BigUint) -> BigUint { - let result = self.modulus() - (value % self.modulus()); - assert_eq!(BigUint::zero(), (&result + value) % self.modulus()); - result - } - -} - -impl FromStr for Prime { - type Err = failure::Error; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "p384" => Ok(Prime::P384), - "p448" => Ok(Prime::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(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(); - let mut y = BigUint::one(); - let mut last_y = BigUint::zero(); - - while !b.is_zero() { - let quot = &a / &b; - 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_y = y; - x = new_x; - y = new_y; - } - (last_x, last_y) -} - -#[cfg(test)] -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))); - } - #[test] - fn check_value_of_p448() { - let one = &BigUint::new(vec![1]); - assert_eq!(Prime::P448.modulus(), &((one << 448) - one - (one << 224))); - } -} \ No newline at end of file diff --git a/src/s4.rs b/src/s4.rs index e640ad2..8f8fcb7 100644 --- a/src/s4.rs +++ b/src/s4.rs @@ -1,11 +1,14 @@ use std::io::{self, prelude::*}; use std::ops::Deref; +use byteorder::{BE, ReadBytesExt, WriteBytesExt}; +use crc::Hasher32; +use failure::err_msg; +use generic_array::GenericArray; +use crate::gf256::GF256; -use num_bigint::BigUint; -use serde::Serialize; +use crate::gf::GF; use crate::poly::Poly; -use crate::primes::Prime; /// Structure @@ -17,12 +20,40 @@ use crate::primes::Prime; /// this way, the high bit is always unset #[derive(Default,Clone, Debug)] pub struct Secret { - length: u16, data: Vec, crc32: u32, } +fn write_varint(mut v: u32, w: &mut Vec) { + while v > 127 { + w.push((v & 0x7F) as u8 | 0x80); + v >>= 7; + } + w.push(v as u8); +} + +fn read_varint(buf: &mut &[u8]) -> Option { + let mut res = 0; + let mut scale = 0; + while buf.len() > 0 && (buf[0] & 0x80 != 0) { + let hd = buf[0]; + eprintln!("Got head byte {hd:02x}"); + res += (hd as u32 & 0x7F).checked_shl(scale)?; + *buf = &buf[1..]; + scale += 7; + } + if buf.len() > 0 { + let hd = buf[0]; + eprintln!("Got head byte {hd:02x}"); + res += (hd as u32 & 0x7F).checked_shl(scale)?; + *buf = &buf[1..]; + Some(res) + } else { + None + } +} + impl Secret { pub fn new(data: Vec) -> Self { if data.len() == 0 { @@ -30,7 +61,6 @@ impl Secret { } let mut res = Secret { - length: data.len() as u16, data, crc32: 0, }; @@ -40,11 +70,12 @@ impl Secret { } fn compute_crc(&self) -> u32 { - use byteorder::{BigEndian, ByteOrder}; use crc::crc32::{CASTAGNOLI, Digest, Hasher32}; let mut hasher = Digest::new(CASTAGNOLI); + let mut size_vi = Vec::with_capacity(5); hasher.reset(); - hasher.write(&[1]); + hasher.write(&[2]); + write_varint(self.data.len() as u32, &mut size_vi); hasher.write(&self.data[..]); hasher.write(&[0, 0, 0, 0]); hasher.sum32() @@ -54,6 +85,38 @@ impl Secret { let crc = self.compute_crc(); crc == self.crc32 } + + pub fn from_buf(mut buf: &[u8]) -> Result { + use failure::err_msg; + if buf[0] != 2 { + return Err(err_msg("Invalid version")) // Invalid version + } + + buf = &buf[1..]; + let size = read_varint(&mut buf) + .ok_or(err_msg("Invalid length"))? + as usize; // Invalid size + if size + 4 > buf.len() { + return Err(err_msg("Unexpected EOF")) // Unexpected EOF + } + let (content, mut buf) = buf.split_at(size); + let data = content.into(); + let crc32 = buf.read_u32::().unwrap(); + + let result = Self { data, crc32 }; + if result.crc32 != result.compute_crc() { + return Err(err_msg("Invalid CRC")) // Invalid CRC + } + + Ok(result) + } + + pub fn to_buf(&self, buf: &mut Vec) { + buf.push(2); // version + write_varint(self.data.len() as u32, &mut *buf); + buf.extend_from_slice(self.data.as_slice()); + buf.write_u32::(self.crc32).unwrap(); + } } impl Deref for Secret { @@ -64,117 +127,88 @@ impl Deref for Secret { } } -impl From for Secret { - fn from(ui: BigUint) -> Self { - use byteorder::{BigEndian, ByteOrder}; - let decoded = ui.to_bytes_be(); - let mut res = Secret::default(); - 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 - } -} - -impl From for BigUint { - fn from(obj: Secret) -> BigUint { - use std::io::Write; - use byteorder::{WriteBytesExt, BigEndian}; - let mut buf = vec![]; - buf.write(&[1]); - buf.write(&obj.data[..]); - buf.write_u32::(obj.crc32); - BigUint::from_bytes_be(&buf[..]) - } -} - // -pub struct Share { - pub prime: crate::primes::Prime, - pub x: BigUint, - pub y: BigUint, +#[derive(Debug)] +pub struct Share { + pub x: Field, + pub y: Vec, } -impl Share { - pub fn new(rnd: &mut Rng, poly: &crate::poly::Poly) -> Self { - use num_bigint::RandBigInt; - let x = rnd.gen_biguint_below(poly.prime.modulus()); - let y = poly.eval_at(&x); +impl Share { + pub fn new(share_no: F, poly: &[Poly]) -> Self { + let x= share_no; + let y = poly.iter().map(|word| word.eval_at(x)).collect(); Share { - prime: poly.prime, x, y, } } pub fn write_to(&self, mut w: W) -> io::Result<()> { - let mut x = self.x.to_bytes_le(); - x.resize(self.prime.byte_size(), 0); - let mut y = self.y.to_bytes_le(); - y.resize(self.prime.byte_size(), 0); - - (&mut w).write_all(&x[..])?; - (&mut w).write_all(&y[..])?; + w.write_all(&self.x.encode()[..])?; + for y in self.y.iter().copied() { + w.write_all(&y.encode()[..])?; + } Ok(()) } - pub fn read_from(mut r: R, prime: crate::primes::Prime) -> io::Result { - let size = prime.byte_size(); - let mut x = vec![0; size]; - (&mut r).read_exact(&mut x[..])?; - let mut x = BigUint::from_bytes_le(&x[..]); + pub fn read_from(mut r: R) -> io::Result { + let mut buf = GenericArray::::default(); + r.read_exact(&mut buf[..])?; + let x = F::decode(buf.clone()); + let mut y = Vec::new(); - let mut y = vec![0; size]; - (&mut r).read_exact(&mut y[..])?; - let mut y = BigUint::from_bytes_le(&y[..]); - - if &x >= prime.modulus() || &y >= prime.modulus() { - return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid point: out of range")); + while let Ok(()) = r.read_exact(&mut buf[..]) { + y.push(F::decode(buf.clone())); } - Ok(Share{ prime, x, y }) + Ok(Share { x, y }) } } -pub fn solve(shares: &[Share]) -> Poly { - let prime = shares[0].prime; - let mut result = Poly::zero(prime); + +pub fn solve(shares: &[Share]) -> Vec> { + // TODO: handle the case where shares have different size + if shares.len() == 0 { + return vec![]; + } + let mut result: Vec> = vec![Poly::::zero(); shares[0].y.len()]; for j in 0..shares.len() { - let mut basis = Poly::zero(prime) + &shares[j].y; + let mut basis: Vec> = shares[j].y.iter().copied().map(|y| Poly::::zero() + y).collect(); 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; + let dx_inv = (shares[j].x - shares[m].x).minv(); // TODO: we can cut runtime in half by sharing (j,m) with (m,j) + for bvec in basis.iter_mut() { + *bvec = (Poly::x() - shares[m].x) * &*bvec * dx_inv; + } } - result += &basis; + result.iter_mut().zip(basis).for_each(|(result, basis)| *result += &basis); } - result.trim(); + + result.iter_mut().for_each(|r| r.trim()); result } -pub fn interpolate0(shares: &[Share]) -> BigUint { +pub fn interpolate0(shares: &[Share]) -> Vec { 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; + let mut result: Vec = vec![F::ZERO; shares[0].y.len()]; + for j in shares { + let mut basis = j.y.clone(); + + for k in shares { + if j.x == k.x { + continue } - basis *= &shares[m].x * - prime.inverse(&shares[m].x + prime.negate(&shares[j].x)); - basis %= prime.modulus(); + let bscale = k.x * (k.x - j.x).minv(); + basis.iter_mut().for_each(|b| { + *b = *b * bscale + }); } - result += basis; + result.iter_mut().zip(basis).for_each(|(r,b)| { + *r = *r + b; + }); } - result % prime.modulus() + result } \ No newline at end of file