Przeglądaj źródła

Initial implementation of Arctic

Ian Goldberg 4 miesięcy temu
rodzic
commit
bfe6dcde04
3 zmienionych plików z 170 dodań i 2 usunięć
  1. 157 0
      src/arctic.rs
  2. 12 2
      src/lagrange.rs
  3. 1 0
      src/main.rs

+ 157 - 0
src/arctic.rs

@@ -0,0 +1,157 @@
+use crate::lagrange::*;
+use crate::shine;
+use curve25519_dalek::ristretto::RistrettoPoint;
+use curve25519_dalek::scalar::Scalar;
+use sha2::Digest;
+use sha2::Sha256;
+
+type PubKey = RistrettoPoint;
+
+pub struct SecKey {
+    n: u32,
+    t: u32,
+    k: u32,
+    sk: Scalar,
+    rk: shine::PreprocKey,
+}
+
+type Signature = (RistrettoPoint, Scalar);
+
+pub fn keygen(n: u32, t: u32) -> (PubKey, Vec<SecKey>) {
+    assert!(t >= 1);
+    assert!(n >= 2 * t - 1);
+
+    let mut seckeys: Vec<SecKey> = Vec::new();
+
+    // The Shine key shares
+    let shinekeys = shine::Key::keygen(n, t);
+
+    // The signature key shares
+    let shamirpoly = ScalarPoly::rand((t as usize) - 1);
+    let pubkey = shine::commit(&shamirpoly.coeffs[0]);
+    for k in 1..n + 1 {
+        seckeys.push(SecKey {
+            n,
+            t,
+            k,
+            sk: shamirpoly.eval(&Scalar::from(k)),
+            rk: shine::PreprocKey::preproc(&shinekeys[(k as usize) - 1]),
+        });
+    }
+
+    (pubkey, seckeys)
+}
+
+fn hash2(combcomm: &RistrettoPoint, pk: &PubKey, msg: &[u8]) -> Scalar {
+    let mut hash = Sha256::new();
+    hash.update(combcomm.compress().as_bytes());
+    hash.update(pk.compress().as_bytes());
+    hash.update(msg);
+    let mut hashval = [0u8; 32];
+    hashval[0..32].copy_from_slice(&hash.finalize());
+    Scalar::from_bytes_mod_order(hashval)
+}
+
+fn hash3(pk: &PubKey, coalition: &[u32], msg: &[u8]) -> [u8; 32] {
+    let mut hash = Sha256::new();
+    hash.update(pk.compress().as_bytes());
+    hash.update(coalition.len().to_le_bytes());
+    for c in coalition {
+        hash.update(c.to_le_bytes());
+    }
+    hash.update(msg);
+    hash.finalize().into()
+}
+
+pub fn sign1(pk: &PubKey, sk: &SecKey, coalition: &[u32], msg: &[u8]) -> RistrettoPoint {
+    assert!(coalition.len() >= 2 * (sk.t as usize) - 1);
+    let w = hash3(pk, coalition, msg);
+    shine::commit(&sk.rk.partialeval(&w))
+}
+
+pub fn sign2_polys(
+    pk: &PubKey,
+    sk: &SecKey,
+    coalition: &[u32],
+    lag_polys: &[ScalarPoly],
+    msg: &[u8],
+    commitments: &[RistrettoPoint],
+) -> Option<Scalar> {
+    // If the inputs are _malformed_, abort
+
+    assert!(coalition.len() == lag_polys.len());
+    assert!(coalition.len() == commitments.len());
+    assert!(coalition.len() >= 2 * (sk.t as usize) - 1);
+
+    // Find my own entry in the coalition; abort if it's not there
+    let kindex = coalition.iter().position(|&k| k == sk.k).unwrap();
+
+    let w = hash3(pk, coalition, msg);
+    let my_eval = sk.rk.partialeval(&w);
+    let my_commit = shine::commit(&my_eval);
+
+    assert!(commitments[kindex] == my_commit);
+
+    // If the inputs are just corrupt values from malicious other
+    // parties, return None but don't crash
+
+    let combcomm = shine::combinecomm_polys(sk.t, lag_polys, commitments)?;
+    let c = hash2(&combcomm, pk, msg);
+
+    Some(my_eval + c * sk.sk)
+}
+
+pub fn sign2(
+    pk: &PubKey,
+    sk: &SecKey,
+    coalition: &[u32],
+    msg: &[u8],
+    commitments: &[RistrettoPoint],
+) -> Option<Scalar> {
+    let polys = lagrange_polys(coalition);
+    sign2_polys(pk, sk, coalition, &polys, msg, commitments)
+}
+
+pub fn combine_polys(
+    pk: &PubKey,
+    t: u32,
+    coalition: &[u32],
+    lag_polys: &[ScalarPoly],
+    msg: &[u8],
+    commitments: &[RistrettoPoint],
+    sigshares: &[Scalar],
+) -> Option<Signature> {
+    assert!(coalition.len() == lag_polys.len());
+    assert!(coalition.len() == commitments.len());
+    assert!(coalition.len() == sigshares.len());
+    assert!(coalition.len() >= 2 * (t as usize) - 1);
+
+    let z = interpolate_polys_0(lag_polys, sigshares);
+
+    // Check the answer
+
+    let combcomm = shine::combinecomm_polys(t, lag_polys, commitments)?;
+    let c = hash2(&combcomm, pk, msg);
+
+    if shine::commit(&z) == combcomm + c * pk {
+        return Some((combcomm, z));
+    }
+    None
+}
+
+pub fn combine(
+    pk: &PubKey,
+    t: u32,
+    coalition: &[u32],
+    msg: &[u8],
+    commitments: &[RistrettoPoint],
+    sigshares: &[Scalar],
+) -> Option<Signature> {
+    let polys = lagrange_polys(coalition);
+    combine_polys(pk, t, coalition, &polys, msg, commitments, sigshares)
+}
+
+pub fn verify(pk: &PubKey, msg: &[u8], sig: &Signature) -> bool {
+    let c = hash2(&sig.0, pk, msg);
+    shine::commit(&sig.1) == sig.0 + c * pk
+}

+ 12 - 2
src/lagrange.rs

@@ -59,7 +59,7 @@ impl ScalarPoly {
     pub fn rand(degree: usize) -> Self {
         let mut rng = rand::thread_rng();
         let mut coeffs: Vec<Scalar> = Vec::new();
-        coeffs.resize_with(degree+1, || { Scalar::random(&mut rng) });
+        coeffs.resize_with(degree + 1, || Scalar::random(&mut rng));
         Self { coeffs }
     }
 
@@ -180,7 +180,7 @@ pub fn test_eval() {
 
 // 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) {
+fn sum_polys_is_x_to_the_i(polys: &[ScalarPoly], i: usize) {
     let mut sum = ScalarPoly::zero();
     for p in polys.iter() {
         sum.add(p);
@@ -218,3 +218,13 @@ pub fn test_lagrange_polys() {
 
     sum_polys_is_x_to_the_i(&polys, 2);
 }
+
+// Interpolate values at x=0 given the pre-computed Lagrange polynomials
+pub fn interpolate_polys_0(lag_polys: &[ScalarPoly], y: &[Scalar]) -> Scalar {
+    assert!(lag_polys.len() == y.len());
+    let mut res = Scalar::zero();
+    for i in 0..y.len() {
+        res += lag_polys[i].coeffs[0] * y[i];
+    }
+    res
+}

+ 1 - 0
src/main.rs

@@ -3,6 +3,7 @@ use rand::RngCore;
 use std::env;
 use std::time::Instant;
 
+pub mod arctic;
 mod lagrange;
 pub mod shine;