Massive rewrite to be based on GF(2^8) rather than a prime field; this results in much faster operation and no size limit

This commit is contained in:
2024-01-14 03:50:39 +01:00
parent 27c0978dea
commit 1b74e60eff
8 changed files with 513 additions and 380 deletions

View File

@@ -2,10 +2,9 @@
name = "rssss"
version = "0.1.0"
authors = ["TQ Hirsch <thequux@thequux.com>"]
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"
serde_cbor = "0.9.0"
serde_json = "1.0.111"
bendy = "0.3.3"
generic-array = "1.0.0"
anyhow = "1.0.79"

31
src/gf.rs Normal file
View File

@@ -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<Output=Self> + Sub<Output=Self> + Mul<Output=Self> + Copy + Clone + Eq + TryFrom<usize> {
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<u8, Self::ChunkSize>) -> Self;
fn encode(self) -> GenericArray<u8, Self::ChunkSize>;
fn decode_slice(s: &[u8]) -> Self {
let mut chunk = GenericArray::<u8, Self::ChunkSize>::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;
}

188
src/gf256.rs Normal file
View File

@@ -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<u8, Self::ChunkSize>) -> Self {
GF256(u8::from_le_bytes(chunk.into_array()))
}
fn encode(self) -> GenericArray<u8, Self::ChunkSize> {
self.0.to_le_bytes().into()
}
fn sample(rng: &mut (impl Rng + ?Sized)) -> Self {
GF256(rng.sample(Standard))
}
}
impl From<u8> for GF256 {
fn from(value: u8) -> Self {
GF256(value)
}
}
impl From<GF256> 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<usize> for GF256 {
type Error = RangeError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
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);
}
}
}

View File

@@ -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;

View File

@@ -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<PathBuf>,
}
@@ -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<PathBuf>,
}
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<rssss::poly::Poly<GF256>> = 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::<Result<Vec<rssss::s4::Share>, Error>>()?;
}).collect::<Result<Vec<rssss::s4::Share<GF256>>, 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<rssss::poly::Poly<GF256>> = serde_cbor::from_reader(io::stdin())?;
let interpolated = poly.into_iter().map(|p| p.eval_at(GF256::ZERO))
.flat_map(|f| f.encode())
.collect::<Vec<_>>();
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::<Result<Vec<rssss::s4::Share>, Error>>()?;
let secret = rssss::s4::Secret::from(rssss::s4::interpolate0(&shares[..]));
}).collect::<Result<Vec<rssss::s4::Share<GF256>>, Error>>()?;
let mut interpolated = rssss::s4::interpolate0(&shares[..]);
let mut secret = Vec::with_capacity(interpolated.len() * <GF256 as GF>::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")

View File

@@ -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<F: GF> {
// coeff[0] + coeff[1]*x + coeff[2] * x^2
// i.e., $$\sum_i coeff_i x^i$$
pub prime: Prime,
coeff: Vec<BigUint>,
coeff: Vec<F>,
}
impl Poly {
pub fn zero(modulus: Prime) -> Self {
impl<F: GF> Poly<F> {
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<F: GF> Add<&Poly<F>> for Poly<F> {
type Output = Poly<F>;
fn add(self, rhs: &Poly) -> Self::Output {
fn add(self, rhs: &Poly<F>) -> 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<F: GF> AddAssign<&Poly<F>> for Poly<F> {
fn add_assign(&mut self, rhs: &Poly<F>) {
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<F: GF> Add for Poly<F> {
type Output = Poly<F>;
fn add(mut self, rhs: &BigUint) -> Self::Output {
fn add(mut self, rhs: Poly<F>) -> Self::Output {
self += &rhs;
self
}
}
impl<F: GF> Add<F> for Poly<F> {
type Output = Poly<F>;
fn add(mut self, rhs: F) -> Self::Output {
self += rhs;
self
}
}
impl AddAssign<&BigUint> for Poly {
fn add_assign(&mut self, rhs: &BigUint) {
impl<F: GF> AddAssign<F> for Poly<F> {
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<BigUint> for Poly {
type Output = Poly;
impl <F: GF> Sub<F> for Poly<F> {
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<BigUint> 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<F: GF> Mul<F> for Poly<F> {
type Output = Poly<F>;
fn mul(mut self, rhs: F) -> Poly<F> {
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<F: GF> Mul<&Poly<F>> for Poly<F> {
type Output = Poly<F>;
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<F>) -> 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<F: GF> Mul<&Poly<F>> for &Poly<F> {
type Output = Poly<F>;
fn mul(self, rhs: &Poly) -> Self::Output {
fn mul(self, rhs: &Poly<F>) -> Self::Output {
self.clone() * rhs
}
}
impl Div<BigUint> for Poly {
type Output = Poly;
impl<F: GF> Div<F> for Poly<F> {
type Output = Poly<F>;
fn div(mut self, rhs: BigUint) -> Poly {
fn div(mut self, rhs: F) -> Poly<F> {
self /= rhs;
self
}
}
impl DivAssign<BigUint> for Poly {
fn div_assign(&mut self, rhs: BigUint) {
let inv = self.prime.inverse(rhs);
impl<F: GF> DivAssign<F> for Poly<F> {
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<F: GF> Neg for Poly<F> {
type Output = Poly<F>;
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<F: GF>{
pub intercept: F,
pub degree: usize
}
impl<F: GF> Distribution<Poly<F>> for UniformPoly<F> {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Poly<F> {
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<F: GF+Serialize> Serialize for Poly<F> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 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<F> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 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<F>;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("list of coefficients")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> 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))
}
}

View File

@@ -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<Self, Self::Err> {
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 * &quot))) % prime.modulus();
let new_y = (last_y + prime.negate(&(&y * &quot))) % 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)));
}
}

208
src/s4.rs
View File

@@ -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<u8>,
crc32: u32,
}
fn write_varint(mut v: u32, w: &mut Vec<u8>) {
while v > 127 {
w.push((v & 0x7F) as u8 | 0x80);
v >>= 7;
}
w.push(v as u8);
}
fn read_varint(buf: &mut &[u8]) -> Option<u32> {
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<u8>) -> 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<Self, failure::Error> {
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::<BE>().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<u8>) {
buf.push(2); // version
write_varint(self.data.len() as u32, &mut *buf);
buf.extend_from_slice(self.data.as_slice());
buf.write_u32::<BE>(self.crc32).unwrap();
}
}
impl Deref for Secret {
@@ -64,117 +127,88 @@ impl Deref for Secret {
}
}
impl From<BigUint> 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<Secret> 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::<BigEndian>(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<Field: GF> {
pub x: Field,
pub y: Vec<Field>,
}
impl Share {
pub fn new<Rng: num_bigint::RandBigInt + ?Sized>(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<F: GF> Share<F> {
pub fn new(share_no: F, poly: &[Poly<F>]) -> 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<W: Write>(&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<R: Read>(mut r: R, prime: crate::primes::Prime) -> io::Result<Self> {
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<R: Read>(mut r: R) -> io::Result<Self> {
let mut buf = GenericArray::<u8, F::ChunkSize>::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<F: GF>(shares: &[Share<F>]) -> Vec<Poly<F>> {
// TODO: handle the case where shares have different size
if shares.len() == 0 {
return vec![];
}
let mut result: Vec<Poly<F>> = vec![Poly::<F>::zero(); shares[0].y.len()];
for j in 0..shares.len() {
let mut basis = Poly::zero(prime) + &shares[j].y;
let mut basis: Vec<Poly<F>> = shares[j].y.iter().copied().map(|y| Poly::<F>::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<F: GF>(shares: &[Share<F>]) -> Vec<F> {
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<F> = 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
}