Skip to content

Commit 19d626d

Browse files
committed
precomputed lookup table to accelerate VSS commit
1 parent 987e87d commit 19d626d

File tree

2 files changed

+58
-28
lines changed

2 files changed

+58
-28
lines changed

timeboost-crypto/src/feldman.rs

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
//! Implementation of Feldman VSS
22
3-
use ark_ec::CurveGroup;
3+
use ark_ec::{CurveGroup, scalar_mul::BatchMulPreprocessing};
44
use ark_poly::{DenseUVPolynomial, Polynomial, univariate::DensePolynomial};
55
use ark_serialize::{CanonicalSerialize, SerializationError, serialize_to_vec};
66
use ark_std::marker::PhantomData;
77
use ark_std::rand::Rng;
8-
use derive_more::{Deref, From, IntoIterator};
8+
use derive_more::{Debug, Deref, From, IntoIterator};
99
use multisig::Committee;
1010
use rayon::prelude::*;
1111
use serde::{Deserialize, Serialize};
1212
use serde_with::serde_as;
13-
use std::{iter::successors, num::NonZeroUsize, ops::Add};
13+
use std::{iter::successors, num::NonZeroUsize, ops::Add, sync::Arc};
1414

1515
use crate::{
1616
interpolation::{interpolate, interpolate_in_exponent},
@@ -21,23 +21,48 @@ use crate::{
2121
#[derive(Debug, Clone)]
2222
pub struct FeldmanVss<C: CurveGroup>(PhantomData<C>);
2323

24-
#[derive(Debug, Clone, Copy)]
25-
pub struct FeldmanVssPublicParam {
24+
#[derive(Debug, Clone)]
25+
pub struct FeldmanVssPublicParam<C: CurveGroup> {
2626
// reconstruction threshold t
2727
pub t: NonZeroUsize,
2828
// total number of nodes
2929
pub n: NonZeroUsize,
30+
31+
// preprocessing lookup table to accelerate batch_mul
32+
#[debug(skip)]
33+
table: Option<Arc<BatchMulPreprocessing<C>>>,
3034
}
3135

32-
impl FeldmanVssPublicParam {
36+
impl<C: CurveGroup> FeldmanVssPublicParam<C> {
3337
pub fn new(t: NonZeroUsize, n: NonZeroUsize) -> Self {
34-
Self { t, n }
38+
Self { t, n, table: None }
3539
}
3640

3741
pub fn from(c: &Committee) -> Self {
3842
Self::new(c.one_honest_threshold(), c.size())
3943
}
4044

45+
/// `Self::from().with_lookup_table()`: this precompute a lookup table for faster commit
46+
pub fn with_lookup_table(self) -> Self {
47+
let table = BatchMulPreprocessing::new(C::generator(), self.t.get());
48+
Self {
49+
t: self.t,
50+
n: self.n,
51+
table: Some(Arc::new(table)),
52+
}
53+
}
54+
55+
/// compute Feldman commit (potentially with accelerated lookup table)
56+
/// ASSUME: coeffs has length t, we skip checks and avoid returning Result, not publicly visble
57+
pub(crate) fn commit(&self, coeffs: &[C::ScalarField]) -> FeldmanCommitment<C> {
58+
if let Some(table) = self.table.as_ref() {
59+
table.batch_mul(coeffs).into()
60+
} else {
61+
let base = C::generator();
62+
FeldmanCommitment::new(coeffs.par_iter().map(|s| base * s).collect::<Vec<_>>())
63+
}
64+
}
65+
4166
pub fn threshold(&self) -> usize {
4267
self.t.get()
4368
}
@@ -50,7 +75,7 @@ impl FeldmanVssPublicParam {
5075
impl<C: CurveGroup> FeldmanVss<C> {
5176
/// sample a random polynomial for VSS `secret`, returns the poly and its feldman commitment
5277
pub(crate) fn rand_poly_and_commit<R: Rng>(
53-
pp: &FeldmanVssPublicParam,
78+
pp: &FeldmanVssPublicParam<C>,
5479
secret: C::ScalarField,
5580
rng: &mut R,
5681
) -> (DensePolynomial<C::ScalarField>, FeldmanCommitment<C>) {
@@ -61,23 +86,23 @@ impl<C: CurveGroup> FeldmanVss<C> {
6186
poly.coeffs[0] = secret;
6287

6388
// prepare commitment, u = (g^a_0, g^a_1, ..., g^a_t-1)
64-
let commitment = C::generator().batch_mul(&poly.coeffs);
89+
let commitment = pp.commit(&poly.coeffs);
6590

66-
(poly, commitment.into())
91+
(poly, commitment)
6792
}
6893

6994
/// given a secret-embedded polynomial, compute the Shamir secret shares
7095
/// node i \in {0,.. ,n-1} get f(i+1)
7196
pub(crate) fn compute_shares(
72-
pp: &FeldmanVssPublicParam,
97+
pp: &FeldmanVssPublicParam<C>,
7398
poly: &DensePolynomial<C::ScalarField>,
7499
) -> impl Iterator<Item = C::ScalarField> {
75100
(0..pp.n.get()).map(|node_idx| poly.evaluate(&((node_idx + 1) as u64).into()))
76101
}
77102

78103
/// same as [`Self::compute_shares()`], but output an iterator of bytes
79104
pub(crate) fn compute_serialized_shares(
80-
pp: &FeldmanVssPublicParam,
105+
pp: &FeldmanVssPublicParam<C>,
81106
poly: &DensePolynomial<C::ScalarField>,
82107
) -> impl Iterator<Item = Vec<u8>> {
83108
Self::compute_shares(pp, poly)
@@ -87,7 +112,7 @@ impl<C: CurveGroup> FeldmanVss<C> {
87112
/// given the Feldman commitment (\vec{u} in paper), compute the `i`-th node's public share,
88113
/// which is g^alpha_i where alpha_i is `i`-th secret share.
89114
pub(crate) fn derive_public_share(
90-
pp: &FeldmanVssPublicParam,
115+
pp: &FeldmanVssPublicParam<C>,
91116
node_idx: usize,
92117
commitment: &[C::Affine],
93118
) -> Result<C, VssError> {
@@ -127,7 +152,7 @@ impl<C: CurveGroup> FeldmanVss<C> {
127152
}
128153

129154
impl<C: CurveGroup> VerifiableSecretSharing for FeldmanVss<C> {
130-
type PublicParam = FeldmanVssPublicParam;
155+
type PublicParam = FeldmanVssPublicParam<C>;
131156
type Secret = C::ScalarField;
132157
type SecretShare = C::ScalarField;
133158
type Commitment = FeldmanCommitment<C>;
@@ -203,6 +228,12 @@ pub struct FeldmanCommitment<C: CurveGroup> {
203228
}
204229

205230
impl<C: CurveGroup> FeldmanCommitment<C> {
231+
/// If you already have value in affine form, use `.into()`
232+
pub fn new(v: Vec<C>) -> Self {
233+
let comm = C::normalize_batch(&v);
234+
Self { comm }
235+
}
236+
206237
pub fn to_bytes(&self) -> Vec<u8> {
207238
bincode::serde::encode_to_vec(self, bincode::config::standard())
208239
.expect("serializing feldman commitment")
@@ -248,7 +279,7 @@ impl<C: CurveGroup> Add<&FeldmanCommitment<C>> for &FeldmanCommitment<C> {
248279

249280
impl<C: CurveGroup> KeyResharing<Self> for FeldmanVss<C> {
250281
fn reshare<R: Rng>(
251-
new_pp: &FeldmanVssPublicParam,
282+
new_pp: &FeldmanVssPublicParam<C>,
252283
old_share: &C::ScalarField,
253284
rng: &mut R,
254285
) -> (Vec<C::ScalarField>, FeldmanCommitment<C>) {
@@ -258,8 +289,8 @@ impl<C: CurveGroup> KeyResharing<Self> for FeldmanVss<C> {
258289
}
259290

260291
fn verify_reshare(
261-
old_pp: &FeldmanVssPublicParam,
262-
new_pp: &FeldmanVssPublicParam,
292+
old_pp: &FeldmanVssPublicParam<C>,
293+
new_pp: &FeldmanVssPublicParam<C>,
263294
send_node_idx: usize,
264295
recv_node_idx: usize,
265296
old_commitment: &FeldmanCommitment<C>,
@@ -279,8 +310,8 @@ impl<C: CurveGroup> KeyResharing<Self> for FeldmanVss<C> {
279310
}
280311

281312
fn combine(
282-
old_pp: &FeldmanVssPublicParam,
283-
new_pp: &FeldmanVssPublicParam,
313+
old_pp: &FeldmanVssPublicParam<C>,
314+
new_pp: &FeldmanVssPublicParam<C>,
284315
recv_node_idx: usize,
285316
reshares: impl ExactSizeIterator<Item = (usize, C::ScalarField, FeldmanCommitment<C>)> + Clone,
286317
) -> Result<(C::ScalarField, FeldmanCommitment<C>), VssError> {

timeboost-crypto/src/vess.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ impl<C: CurveGroup> ShoupVess<C> {
240240
/// IOPattern binds all public parameters including N, M, t, n, aad, to avoid weak FS attack.
241241
fn io_pattern(
242242
&self,
243-
vss_pp: &FeldmanVssPublicParam,
243+
vss_pp: &FeldmanVssPublicParam<C>,
244244
aad: &[u8],
245245
mode: &Mode<C>,
246246
) -> spongefish::DomainSeparator {
@@ -341,7 +341,7 @@ impl<C: CurveGroup> ShoupVess<C> {
341341
// each dealing contains (Shamir poly + Feldman commitment + MRE ciphertext)
342342
fn new_dealing<'a, I>(
343343
&self,
344-
vss_pp: &FeldmanVssPublicParam,
344+
vss_pp: &FeldmanVssPublicParam<C>,
345345
ith: usize,
346346
seed: &[u8; 32],
347347
recipients: I,
@@ -395,7 +395,7 @@ impl<C: CurveGroup> ShoupVess<C> {
395395
{
396396
// input validation - check length without consuming the iterator
397397
let recipients_iter = recipients.into_iter();
398-
let vss_pp = FeldmanVssPublicParam::from(committee);
398+
let vss_pp = FeldmanVssPublicParam::from(committee).with_lookup_table();
399399
let n = vss_pp.num_nodes();
400400
if recipients_iter.len() != n {
401401
return Err(VessError::WrongRecipientsLength(n, recipients_iter.len()));
@@ -493,7 +493,7 @@ impl<C: CurveGroup> ShoupVess<C> {
493493
I: IntoIterator<Item = &'a mre::EncryptionKey<C>> + Clone,
494494
I::IntoIter: ExactSizeIterator,
495495
{
496-
let vss_pp = FeldmanVssPublicParam::from(committee);
496+
let vss_pp = FeldmanVssPublicParam::from(committee).with_lookup_table();
497497
let mut verifier_state = self
498498
.io_pattern(&vss_pp, aad, &mode)
499499
.to_verifier_state(&ct.transcript);
@@ -517,11 +517,10 @@ impl<C: CurveGroup> ShoupVess<C> {
517517
match next_subset_idx {
518518
Some(j) if i == *j => {
519519
// k in S, shift the commitment
520-
let shifted_comm = C::generator().batch_mul(
521-
shifted_polys
520+
let shifted_comm = vss_pp.commit(
521+
&shifted_polys
522522
.pop_front()
523-
.expect("subset_size > 0, so is shifted_polys.len()")
524-
.as_ref(),
523+
.expect("subset_size > 0, so is shifted_polys.len()"),
525524
);
526525

527526
let mut unshifted_comm = vec![];
@@ -566,7 +565,7 @@ impl<C: CurveGroup> ShoupVess<C> {
566565
// Verifier's core logic, shared between `verify()` and `decrypt()`.
567566
fn verify_core(
568567
&self,
569-
vss_pp: &FeldmanVssPublicParam,
568+
vss_pp: &FeldmanVssPublicParam<C>,
570569
verifier_state: &mut VerifierState,
571570
mode: &Mode<C>,
572571
) -> Result<ProverMessage<C>, VessError> {

0 commit comments

Comments
 (0)