Parcourir la source

Initial Shine implementation

Ian Goldberg il y a 4 mois
commit
270aa4fc98
4 fichiers modifiés avec 531 ajouts et 0 suppressions
  1. 11 0
      Cargo.toml
  2. 166 0
      src/lagrange.rs
  3. 57 0
      src/main.rs
  4. 297 0
      src/shine.rs

+ 11 - 0
Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "arctic"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+curve25519-dalek = "2"
+rand = "0.7"
+sha2 = "0.9"

+ 166 - 0
src/lagrange.rs

@@ -0,0 +1,166 @@
+use curve25519_dalek::scalar::Scalar;
+
+// Versions that just compute coefficients; these are used if you know
+// all of your input points are correct
+
+// Compute the Lagrange coefficient for the value at the given x
+// coordinate, to interpolate the value at target_x.  The x coordinates
+// in the coalition are allowed to include x itself, which will be
+// ignored.
+pub fn lagrange(coalition: &[u32], x: u32, target_x: u32) -> Scalar {
+    let mut numer = Scalar::one();
+    let mut denom = Scalar::one();
+    let xscal = Scalar::from(x);
+    let target_xscal = Scalar::from(target_x);
+    for &c in coalition {
+        if c != x {
+            let cscal = Scalar::from(c);
+            numer *= target_xscal - cscal;
+            denom *= xscal - cscal;
+        }
+    }
+    numer * denom.invert()
+}
+
+// Interpolate the given (x,y) coordinates at the target x value.
+// target_x must _not_ be in the x slice
+pub fn interpolate(x: &[u32], y: &[Scalar], target_x: u32) -> Scalar {
+    assert!(x.len() == y.len());
+    let mut res = Scalar::zero();
+    for i in 0..x.len() {
+        let lag_coeff = lagrange(x, x[i], target_x);
+        res += lag_coeff * y[i];
+    }
+    res
+}
+
+// Versions that compute the entire Lagrange polynomials; these are used
+// if need to _check_ that all of your input points are correct.
+
+// A ScalarPoly represents a polynomial whose coefficients are scalars.
+// The coeffs vector has length (deg+1), where deg is the degree of the
+// polynomial.  coeffs[i] is the coefficient on x^i.
+#[derive(Clone, Debug)]
+pub struct ScalarPoly {
+    pub coeffs: Vec<Scalar>,
+}
+
+impl ScalarPoly {
+    pub fn zero() -> Self {
+        Self { coeffs: vec![] }
+    }
+
+    pub fn one() -> Self {
+        Self {
+            coeffs: vec![Scalar::one()],
+        }
+    }
+
+    // Multiply self by the polynomial (x+a), for the given a
+    pub fn mult_x_plus_a(&mut self, a: &Scalar) {
+        // The length of coeffs is (deg+1), which is what we want the
+        // new degree to be
+        let newdeg = self.coeffs.len();
+        if newdeg == 0 {
+            // self is the zero polynomial, so it doesn't change with
+            // the multiplication by x+a
+            return;
+        }
+        let newcoeffs = (0..newdeg + 1)
+            .map(|i| {
+                if i == 0 {
+                    self.coeffs[i] * a
+                } else if i == newdeg {
+                    self.coeffs[i - 1]
+                } else {
+                    self.coeffs[i - 1] + self.coeffs[i] * a
+                }
+            })
+            .collect();
+        self.coeffs = newcoeffs;
+    }
+
+    // Multiply self by the constant c
+    pub fn mult_scalar(&mut self, c: &Scalar) {
+        for coeff in self.coeffs.iter_mut() {
+            *coeff *= c;
+        }
+    }
+
+    // Add another ScalarPoly to this one
+    pub fn add(&mut self, other: &Self) {
+        if other.coeffs.len() > self.coeffs.len() {
+            self.coeffs.resize(other.coeffs.len(), Scalar::zero());
+        }
+        for i in 0..other.coeffs.len() {
+            self.coeffs[i] += other.coeffs[i];
+        }
+    }
+}
+
+// Compute the Lagrange polynomial for the value at the given x
+// coordinate.  The x coordinates in the coalition are allowed to
+// include x itself, which will be ignored.
+pub fn lagrange_poly(coalition: &[u32], x: u32) -> ScalarPoly {
+    let mut numer = ScalarPoly::one();
+    let mut denom = Scalar::one();
+    let xscal = Scalar::from(x);
+    for &c in coalition {
+        if c != x {
+            let cscal = Scalar::from(c);
+            numer.mult_x_plus_a(&-cscal);
+            denom *= xscal - cscal;
+        }
+    }
+    numer.mult_scalar(&denom.invert());
+    numer
+}
+
+// Compute the full set of Lagrange polynomials for the given coalition
+pub fn lagrange_polys(coalition: &[u32]) -> Vec<ScalarPoly> {
+    coalition
+        .iter()
+        .map(|&x| lagrange_poly(coalition, x))
+        .collect()
+}
+
+// Check that the sum of the given polys is just x^i
+#[cfg(test)]
+fn sum_polys_is_x_to_the_i(polys: &Vec<ScalarPoly>, i: usize) {
+    let mut sum = ScalarPoly::zero();
+    for p in polys.iter() {
+        sum.add(p);
+    }
+    println!("sum = {:?}", sum);
+    for j in 0..sum.coeffs.len() {
+        assert!(
+            sum.coeffs[j]
+                == if i == j {
+                    Scalar::one()
+                } else {
+                    Scalar::zero()
+                }
+        );
+    }
+}
+
+#[test]
+pub fn test_lagrange_polys() {
+    let coalition: Vec<u32> = vec![1, 2, 5, 8, 12, 14];
+
+    let mut polys = lagrange_polys(&coalition);
+
+    sum_polys_is_x_to_the_i(&polys, 0);
+
+    for i in 0..coalition.len() {
+        polys[i].mult_scalar(&Scalar::from(coalition[i]));
+    }
+
+    sum_polys_is_x_to_the_i(&polys, 1);
+
+    for i in 0..coalition.len() {
+        polys[i].mult_scalar(&Scalar::from(coalition[i]));
+    }
+
+    sum_polys_is_x_to_the_i(&polys, 2);
+}

+ 57 - 0
src/main.rs

@@ -0,0 +1,57 @@
+use curve25519_dalek::constants as dalek_constants;
+use rand::RngCore;
+use std::env;
+use std::time::Instant;
+
+mod lagrange;
+pub mod shine;
+
+fn main() {
+    let args: Vec<String> = env::args().collect();
+    if args.len() < 4 || args.len() > 5 {
+        println!("Usage: {} n t wlen [reps]", args[0]);
+        return;
+    }
+    let n: u32 = args[1].parse().unwrap();
+    let t: u32 = args[2].parse().unwrap();
+    let wlen: usize = args[3].parse().unwrap();
+    let mut reps = 1usize;
+    if args.len() > 4 {
+        reps = args[4].parse().unwrap();
+    }
+    let mut wvec: Vec<u8> = Vec::new();
+    let mut rng = rand::thread_rng();
+    wvec.resize(wlen, 0);
+    let generator = dalek_constants::RISTRETTO_BASEPOINT_TABLE;
+
+    let rk = shine::PreprocKey::rand(n, t);
+    let delta = rk.delta();
+
+    let mut evaltotdur = 0u128;
+    let mut commtotdur = 0u128;
+
+    for _ in 0..reps {
+        rng.fill_bytes(&mut wvec);
+        let evalstart = Instant::now();
+        let evaluation = rk.partialeval(&wvec);
+        let evaldur = evalstart.elapsed().as_micros();
+        evaltotdur += evaldur;
+
+        let commstart = Instant::now();
+        let _commitment = &evaluation * &generator;
+        let commdur = commstart.elapsed().as_micros();
+        commtotdur += commdur;
+    }
+
+    println!(
+        "{} {} {} {} {} {} {} {}",
+        n,
+        t,
+        wlen,
+        reps,
+        delta,
+        (evaltotdur as usize) / reps,
+        (commtotdur as usize) / reps,
+        (evaltotdur as f64) / ((reps as f64) * (delta as f64))
+    );
+}

+ 297 - 0
src/shine.rs

@@ -0,0 +1,297 @@
+use crate::lagrange::*;
+use curve25519_dalek::constants as dalek_constants;
+use curve25519_dalek::ristretto::RistrettoPoint;
+use curve25519_dalek::scalar::Scalar;
+use curve25519_dalek::traits::Identity;
+use rand::RngCore;
+use sha2::Digest;
+use sha2::Sha256;
+
+// Compute (m choose k) when m^k < 2^64
+fn binom(m: u32, k: u32) -> u64 {
+    let mut numer = 1u64;
+    let mut denom = 1u64;
+    for i in 0u64..(k as u64) {
+        numer *= (m as u64) - i;
+        denom *= i + 1;
+    }
+    numer / denom
+}
+
+fn hash1(theta: &[u8; 16], w: &[u8]) -> Scalar {
+    let mut hash = Sha256::new();
+    hash.update(&theta);
+    hash.update(&w);
+    let mut hashval = [0u8; 32];
+    hashval[0..32].copy_from_slice(&hash.finalize());
+    Scalar::from_bytes_mod_order(hashval)
+}
+
+// Iterate over subsets of {1, 2, ..., n} of size k
+struct SubsetIter {
+    n: u32,
+    k: u32,
+    done: bool,
+    nextsubset: Vec<u32>,
+}
+
+impl SubsetIter {
+    pub fn new(n: u32, k: u32) -> Self {
+        assert!(k >= 1);
+        assert!(n >= k);
+        let nextsubset: Vec<u32> = (1..(k + 1)).collect();
+        Self {
+            n,
+            k,
+            done: false,
+            nextsubset,
+        }
+    }
+}
+
+impl Iterator for SubsetIter {
+    type Item = Vec<u32>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let mut incindex: usize = 0;
+        let ku = self.k as usize;
+        let res = if self.done {
+            None
+        } else {
+            Some(self.nextsubset.clone())
+        };
+        while incindex < ku && self.nextsubset[ku - 1 - incindex] == self.n - (incindex as u32) {
+            incindex += 1;
+        }
+        if incindex < ku {
+            self.nextsubset[ku - 1 - incindex] += 1;
+            for i in 0..incindex {
+                self.nextsubset[ku - incindex + i] =
+                    self.nextsubset[ku - 1 - incindex] + (i as u32) + 1;
+            }
+        } else {
+            self.done = true;
+        }
+        res
+    }
+}
+
+#[test]
+pub fn test_subsetiter() {
+    let si = SubsetIter::new(7, 4);
+
+    for v in si {
+        println!("{:?}", v);
+    }
+}
+
+// The key for player k will consist of a vector of (v, theta) tuples,
+// where the v values enumerate all lists of t-1 player numbers (from
+// 1 to n) that do _not_ include k
+#[derive(Debug)]
+pub struct Key {
+    pub n: u32,
+    pub t: u32,
+    pub k: u32,
+    pub secrets: Vec<(Vec<u32>, [u8; 16])>,
+}
+
+impl Key {
+    pub fn keygen(n: u32, t: u32) -> Vec<Self> {
+        let mut rng = rand::thread_rng();
+        let mut res: Vec<Self> = Vec::new();
+        for k in 1..(n + 1) {
+            res.push(Self {
+                n,
+                t,
+                k,
+                secrets: Vec::new(),
+            });
+        }
+        let si = SubsetIter::new(n, t - 1);
+
+        for v in si {
+            // For each subset of size t-1, pick a random secret, and
+            // give it to all players _not_ in that subset
+            let mut theta: [u8; 16] = [0; 16];
+            rng.fill_bytes(&mut theta);
+            let mut vnextind = 0usize;
+            let mut vnext = v[0];
+            for i in 1..(n + 1) {
+                if i < vnext {
+                    res[(i - 1) as usize]
+                        .secrets
+                        .push((v.clone(), theta.clone()));
+                } else {
+                    vnextind += 1;
+                    vnext = if vnextind < ((t - 1) as usize) {
+                        v[vnextind]
+                    } else {
+                        n + 1
+                    };
+                }
+            }
+        }
+        res
+    }
+}
+
+#[test]
+pub fn test_keygen() {
+    let keys = Key::keygen(7, 4);
+
+    println!("key for player 3: {:?}", keys[2]);
+    println!("key for player 7: {:?}", keys[6]);
+}
+
+#[derive(Debug)]
+pub struct PreprocKey {
+    pub n: u32,
+    pub t: u32,
+    pub k: u32,
+    pub secrets: Vec<([u8; 16], Scalar)>,
+}
+
+impl PreprocKey {
+    pub fn preproc(key: &Key) -> Self {
+        Self {
+            n: key.n,
+            t: key.t,
+            k: key.k,
+            secrets: key
+                .secrets
+                .iter()
+                .map(|(v, theta)| (theta.clone(), lagrange(&v, 0, key.k)))
+                .collect(),
+        }
+    }
+
+    pub fn rand(n: u32, t: u32) -> Self {
+        let delta = binom(n - 1, t - 1);
+        let mut secrets: Vec<([u8; 16], Scalar)> = Vec::new();
+        let mut rng = rand::thread_rng();
+        for _ in 0u64..delta {
+            let mut theta = [0u8; 16];
+            rng.fill_bytes(&mut theta);
+            let lagrange: Scalar = Scalar::random(&mut rng);
+            secrets.push((theta, lagrange));
+        }
+        Self {
+            n,
+            t,
+            k: 1,
+            secrets,
+        }
+    }
+
+    pub fn partialeval(&self, w: &[u8]) -> Scalar {
+        let mut res = Scalar::zero();
+        for &(theta, lagrange) in &self.secrets {
+            res += hash1(&theta, &w) * lagrange;
+        }
+        res
+    }
+
+    pub fn delta(&self) -> usize {
+        self.secrets.len()
+    }
+}
+
+pub fn commit(evaluation: &Scalar) -> RistrettoPoint {
+    evaluation * &dalek_constants::RISTRETTO_BASEPOINT_TABLE
+}
+
+// Combine commitments using precomputed Lagrange polynomials.  Return
+// None if the commitments are not consistent with the given t.  You
+// must pass at least 2t-1 commitments, and the same number of
+// lag_polys.
+pub fn combinecomm_polys(
+    t: u32,
+    lag_polys: &[ScalarPoly],
+    commitments: &[RistrettoPoint],
+) -> Option<RistrettoPoint> {
+    // Check if the commitments are consistent: when interpolating the
+    // polys in the exponent, the low t coefficients can be non-0 but
+    // the ones above that must be 0
+
+    let mu = commitments.len();
+    assert!(t >= 1);
+    assert!(mu >= 2 * (t as usize) - 1);
+    assert!(mu == lag_polys.len());
+    assert!(mu == lag_polys[0].coeffs.len());
+    for i in (t as usize)..mu {
+        let mut bi = RistrettoPoint::identity();
+        for j in 0..mu {
+            bi += lag_polys[j].coeffs[i] * commitments[j];
+        }
+        if bi != RistrettoPoint::identity() {
+            return None;
+        }
+    }
+    let mut res = RistrettoPoint::identity();
+    for j in 0..mu {
+        res += lag_polys[j].coeffs[0] * commitments[j];
+    }
+    Some(res)
+}
+
+// Combine commitments. Return None if the commitments are not
+// consistent with the given t.  You must pass at least 2t-1
+// commitments, and the same size of coalition.
+pub fn combinecomm(
+    t: u32,
+    coalition: &[u32],
+    commitments: &[RistrettoPoint],
+) -> Option<RistrettoPoint> {
+    let polys = lagrange_polys(coalition);
+    combinecomm_polys(t, &polys, commitments)
+}
+
+#[test]
+pub fn test_preproc() {
+    let keys = Key::keygen(7, 4);
+
+    let ppkey3 = PreprocKey::preproc(&keys[2]);
+    let ppkey7 = PreprocKey::preproc(&keys[6]);
+
+    println!("preproc key for player 3: {:?}", ppkey3);
+    println!("preproc key for player 7: {:?}", ppkey7);
+}
+
+#[test]
+pub fn test_partialeval() {
+    let keys = Key::keygen(7, 3);
+    let ppkeys: Vec<PreprocKey> = keys.iter().map(|x| PreprocKey::preproc(x)).collect();
+    let mut rng = rand::thread_rng();
+    let mut w = [0u8; 32];
+    rng.fill_bytes(&mut w);
+    let evals: Vec<Scalar> = ppkeys.iter().map(|k| k.partialeval(&w)).collect();
+
+    // Try interpolating different subsets and check that the answer is
+    // the same
+    let interp1 = interpolate(&vec![1, 2, 3, 4, 5], &evals[0..5], 0);
+    let interp2 = interpolate(&vec![3, 4, 5, 6, 7], &evals[2..7], 0);
+    println!("interp1 = {:?}", interp1);
+    println!("interp2 = {:?}", interp2);
+    assert!(interp1 == interp2);
+}
+
+#[test]
+pub fn test_combinecomm() {
+    let keys = Key::keygen(7, 3);
+    let ppkeys: Vec<PreprocKey> = keys.iter().map(|x| PreprocKey::preproc(x)).collect();
+    let mut rng = rand::thread_rng();
+    let mut w = [0u8; 32];
+    rng.fill_bytes(&mut w);
+    let commitments: Vec<RistrettoPoint> =
+        ppkeys.iter().map(|k| commit(&k.partialeval(&w))).collect();
+
+    let comm1 = combinecomm(3, &vec![1, 2, 3, 4, 5], &commitments[0..5]);
+    let comm2 = combinecomm(3, &vec![3, 4, 5, 6, 7], &commitments[2..7]);
+    assert_ne!(comm1, None);
+    assert_ne!(comm2, None);
+
+    // Test a failure case
+    let comm3 = combinecomm(3, &vec![1, 2, 3, 4, 6], &commitments[0..5]);
+    assert_eq!(comm3, None);
+}