@@ -6,13 +6,14 @@ use ark_serialize::{CanonicalSerialize, SerializationError, serialize_to_vec};
6
6
use ark_std:: marker:: PhantomData ;
7
7
use ark_std:: rand:: Rng ;
8
8
use derive_more:: { Deref , From , IntoIterator } ;
9
+ use rayon:: prelude:: * ;
9
10
use serde:: { Deserialize , Serialize } ;
10
11
use serde_with:: serde_as;
11
- use std:: { iter:: successors, num:: NonZeroUsize } ;
12
+ use std:: { iter:: successors, num:: NonZeroUsize , ops :: Add } ;
12
13
13
14
use crate :: {
14
- interpolation:: interpolate,
15
- traits:: dkg:: { VerifiableSecretSharing , VssError } ,
15
+ interpolation:: { interpolate, interpolate_in_exponent } ,
16
+ traits:: dkg:: { KeyResharing , VerifiableSecretSharing , VssError } ,
16
17
} ;
17
18
18
19
/// Feldman VSS: <https://www.cs.umd.edu/~gasarch/TOPICS/secretsharing/feldmanVSS.pdf>
@@ -152,27 +153,26 @@ impl<C: CurveGroup> VerifiableSecretSharing for FeldmanVss<C> {
152
153
153
154
fn reconstruct (
154
155
pp : & Self :: PublicParam ,
155
- shares : impl Iterator < Item = ( usize , Self :: SecretShare ) > ,
156
+ shares : impl ExactSizeIterator < Item = ( usize , Self :: SecretShare ) > + Clone ,
156
157
) -> Result < Self :: Secret , VssError > {
157
- let shares = shares. collect :: < Vec < _ > > ( ) ;
158
158
let n = pp. n . get ( ) ;
159
159
let t = pp. t . get ( ) ;
160
160
// input validation
161
161
if shares. len ( ) != t {
162
162
return Err ( VssError :: MismatchedSharesCount ( t, shares. len ( ) ) ) ;
163
163
}
164
- for ( idx, _) in shares. iter ( ) {
165
- if * idx >= n {
166
- return Err ( VssError :: IndexOutOfBound ( n - 1 , * idx) ) ;
164
+ for ( idx, _) in shares. clone ( ) {
165
+ if idx >= n {
166
+ return Err ( VssError :: IndexOutOfBound ( n - 1 , idx) ) ;
167
167
}
168
168
}
169
169
170
170
// Lagrange interpolate to get back the secret
171
171
let eval_points: Vec < _ > = shares
172
- . iter ( )
173
- . map ( |& ( idx, _) | C :: ScalarField :: from ( idx as u64 + 1 ) )
172
+ . clone ( )
173
+ . map ( |( idx, _) | C :: ScalarField :: from ( idx as u64 + 1 ) )
174
174
. collect ( ) ;
175
- let evals: Vec < _ > = shares. iter ( ) . map ( |& ( _, share) | share) . collect ( ) ;
175
+ let evals: Vec < _ > = shares. map ( |( _, share) | share) . collect ( ) ;
176
176
interpolate :: < C > ( & eval_points, & evals)
177
177
. map_err ( |e| VssError :: FailedReconstruction ( e. to_string ( ) ) )
178
178
}
@@ -206,15 +206,130 @@ impl<C: CurveGroup> FeldmanCommitment<C> {
206
206
pub fn try_from_bytes < const N : usize > ( value : & [ u8 ] ) -> Result < Self , SerializationError > {
207
207
crate :: try_from_bytes :: < Self , N > ( value)
208
208
}
209
-
210
209
pub fn try_from_str < const N : usize > ( value : & str ) -> Result < Self , SerializationError > {
211
210
crate :: try_from_str :: < Self , N > ( value)
212
211
}
213
212
}
214
213
214
+ impl < C : CurveGroup > Add < FeldmanCommitment < C > > for FeldmanCommitment < C > {
215
+ type Output = FeldmanCommitment < C > ;
216
+
217
+ fn add ( self , other : FeldmanCommitment < C > ) -> Self :: Output {
218
+ & self + & other
219
+ }
220
+ }
221
+
222
+ impl < C : CurveGroup > Add < & FeldmanCommitment < C > > for FeldmanCommitment < C > {
223
+ type Output = FeldmanCommitment < C > ;
224
+
225
+ fn add ( self , other : & FeldmanCommitment < C > ) -> Self :: Output {
226
+ & self + other
227
+ }
228
+ }
229
+
230
+ impl < C : CurveGroup > Add < & FeldmanCommitment < C > > for & FeldmanCommitment < C > {
231
+ type Output = FeldmanCommitment < C > ;
232
+
233
+ fn add ( self , other : & FeldmanCommitment < C > ) -> Self :: Output {
234
+ let combined: Vec < C > = self
235
+ . comm
236
+ . iter ( )
237
+ . zip ( other. comm . iter ( ) )
238
+ . map ( |( x, y) | * x + y)
239
+ . collect ( ) ;
240
+ C :: normalize_batch ( & combined) . into ( )
241
+ }
242
+ }
243
+
244
+ impl < C : CurveGroup > KeyResharing < Self > for FeldmanVss < C > {
245
+ fn reshare < R : Rng > (
246
+ new_pp : & FeldmanVssPublicParam ,
247
+ old_share : & C :: ScalarField ,
248
+ rng : & mut R ,
249
+ ) -> ( Vec < C :: ScalarField > , FeldmanCommitment < C > ) {
250
+ let ( poly, comm) = Self :: rand_poly_and_commit ( new_pp, * old_share, rng) ;
251
+ let reshares = Self :: compute_shares ( new_pp, & poly) . collect ( ) ;
252
+ ( reshares, comm)
253
+ }
254
+
255
+ fn verify_reshare (
256
+ old_pp : & FeldmanVssPublicParam ,
257
+ new_pp : & FeldmanVssPublicParam ,
258
+ send_node_idx : usize ,
259
+ recv_node_idx : usize ,
260
+ old_commitment : & FeldmanCommitment < C > ,
261
+ row_commitment : & FeldmanCommitment < C > ,
262
+ reshare : & C :: ScalarField ,
263
+ ) -> Result < ( ) , VssError > {
264
+ let old_public_share = Self :: derive_public_share ( old_pp, send_node_idx, old_commitment) ?;
265
+ let new_public_share = Self :: derive_public_share ( new_pp, recv_node_idx, row_commitment) ?;
266
+
267
+ if C :: generator ( ) . mul ( reshare) == new_public_share
268
+ && row_commitment[ 0 ] == old_public_share. into_affine ( )
269
+ {
270
+ Ok ( ( ) )
271
+ } else {
272
+ Err ( VssError :: FailedVerification )
273
+ }
274
+ }
275
+
276
+ fn combine (
277
+ old_pp : & FeldmanVssPublicParam ,
278
+ new_pp : & FeldmanVssPublicParam ,
279
+ recv_node_idx : usize ,
280
+ reshares : impl ExactSizeIterator < Item = ( usize , C :: ScalarField , FeldmanCommitment < C > ) > + Clone ,
281
+ ) -> Result < ( C :: ScalarField , FeldmanCommitment < C > ) , VssError > {
282
+ // input validation
283
+ let n = old_pp. n . get ( ) ;
284
+ if reshares. len ( ) == 0 {
285
+ return Err ( VssError :: EmptyReshare ) ;
286
+ }
287
+ for ( idx, _, _) in reshares. clone ( ) {
288
+ if idx >= n {
289
+ return Err ( VssError :: IndexOutOfBound ( n - 1 , idx) ) ;
290
+ }
291
+ }
292
+
293
+ let new_n = new_pp. n . get ( ) ;
294
+ let new_t = new_pp. t . get ( ) ;
295
+ if recv_node_idx >= new_n {
296
+ return Err ( VssError :: IndexOutOfBound ( new_n - 1 , recv_node_idx) ) ;
297
+ }
298
+ for ( _, _, row_commitment) in reshares. clone ( ) {
299
+ if row_commitment. len ( ) != new_t {
300
+ return Err ( VssError :: InvalidCommitment ) ;
301
+ }
302
+ }
303
+
304
+ // interpolate reshares to get new secret share
305
+ let eval_points: Vec < _ > = reshares
306
+ . clone ( )
307
+ . map ( |( idx, _, _) | C :: ScalarField :: from ( idx as u64 + 1 ) )
308
+ . collect ( ) ;
309
+ let recv_reshares: Vec < _ > = reshares. clone ( ) . map ( |( _, share, _) | share) . collect ( ) ;
310
+ let new_secret = interpolate :: < C > ( & eval_points, & recv_reshares)
311
+ . map_err ( |e| VssError :: FailedCombine ( e. to_string ( ) ) ) ?;
312
+
313
+ // interpolate in the exponent to get new Feldman commitment
314
+ let row_commitments: Vec < _ > = reshares. map ( |( _, _, commitment) | commitment) . collect ( ) ;
315
+ let new_commitment = ( 0 ..new_t)
316
+ . into_par_iter ( )
317
+ . map ( |j| {
318
+ let j_th_coeffs: Vec < C :: Affine > =
319
+ row_commitments. iter ( ) . map ( |row| row[ j] ) . collect ( ) ;
320
+ interpolate_in_exponent :: < C > ( & eval_points, & j_th_coeffs)
321
+ . map_err ( |e| VssError :: FailedCombine ( e. to_string ( ) ) )
322
+ } )
323
+ . collect :: < Result < Vec < _ > , VssError > > ( ) ?;
324
+ let new_commitment = C :: normalize_batch ( & new_commitment) ;
325
+
326
+ Ok ( ( new_secret, new_commitment. into ( ) ) )
327
+ }
328
+ }
329
+
215
330
#[ cfg( test) ]
216
331
mod tests {
217
- use ark_bls12_381:: G1Projective ;
332
+ use ark_bls12_381:: { Fr , G1Projective } ;
218
333
use ark_std:: { UniformRand , rand:: seq:: SliceRandom , test_rng} ;
219
334
220
335
use super :: * ;
@@ -297,4 +412,160 @@ mod tests {
297
412
fn test_feldman_vss ( ) {
298
413
test_feldman_vss_helper :: < G1Projective > ( ) ;
299
414
}
415
+
416
+ // Core key resharing workflow
417
+ fn run_reshare_scenario (
418
+ old_t : usize ,
419
+ old_n : usize ,
420
+ new_t : usize ,
421
+ new_n : usize ,
422
+ rng : & mut impl Rng ,
423
+ ) {
424
+ let old_pp = FeldmanVssPublicParam :: new (
425
+ NonZeroUsize :: new ( old_t) . unwrap ( ) ,
426
+ NonZeroUsize :: new ( old_n) . unwrap ( ) ,
427
+ ) ;
428
+ let new_pp = FeldmanVssPublicParam :: new (
429
+ NonZeroUsize :: new ( new_t) . unwrap ( ) ,
430
+ NonZeroUsize :: new ( new_n) . unwrap ( ) ,
431
+ ) ;
432
+
433
+ let secret = Fr :: rand ( rng) ;
434
+
435
+ let ( old_shares, old_commitment) = FeldmanVss :: < G1Projective > :: share ( & old_pp, rng, secret) ;
436
+
437
+ // Verify original shares
438
+ for ( node_idx, share) in old_shares. iter ( ) . enumerate ( ) {
439
+ assert ! (
440
+ FeldmanVss :: <G1Projective >:: verify( & old_pp, node_idx, share, & old_commitment)
441
+ . is_ok( )
442
+ ) ;
443
+ }
444
+
445
+ let mut reshare_matrix = Vec :: new ( ) ;
446
+ let mut row_commitments = Vec :: new ( ) ;
447
+
448
+ for old_share in old_shares. iter ( ) {
449
+ let ( reshare_row, row_commitment) =
450
+ FeldmanVss :: < G1Projective > :: reshare ( & new_pp, old_share, rng) ;
451
+ reshare_matrix. push ( reshare_row) ;
452
+ row_commitments. push ( row_commitment) ;
453
+ }
454
+
455
+ // Verify reshares
456
+ for i in 0 ..old_n {
457
+ for j in 0 ..new_n {
458
+ assert ! (
459
+ FeldmanVss :: <G1Projective >:: verify_reshare(
460
+ & old_pp,
461
+ & new_pp,
462
+ i,
463
+ j,
464
+ & old_commitment,
465
+ & row_commitments[ i] ,
466
+ & reshare_matrix[ i] [ j] ,
467
+ )
468
+ . is_ok( )
469
+ ) ;
470
+ }
471
+ }
472
+
473
+ let mut new_shares = Vec :: new ( ) ;
474
+ let mut new_commitments = Vec :: new ( ) ;
475
+
476
+ for j in 0 ..new_n {
477
+ let recv_reshares: Vec < Fr > = ( 0 ..old_t)
478
+ . collect :: < Vec < _ > > ( )
479
+ . iter ( )
480
+ . map ( |& i| reshare_matrix[ i] [ j] )
481
+ . collect ( ) ;
482
+ let selected_row_commitments: Vec < FeldmanCommitment < _ > > =
483
+ ( 0 ..old_t) . map ( |i| row_commitments[ i] . clone ( ) ) . collect ( ) ;
484
+
485
+ let reshares_iter =
486
+ ( 0 ..old_t) . map ( |i| ( i, recv_reshares[ i] , selected_row_commitments[ i] . clone ( ) ) ) ;
487
+ let ( new_secret_share, new_commitment) =
488
+ FeldmanVss :: < G1Projective > :: combine ( & old_pp, & new_pp, j, reshares_iter) . unwrap ( ) ;
489
+
490
+ new_shares. push ( new_secret_share) ;
491
+ new_commitments. push ( new_commitment) ;
492
+
493
+ assert ! (
494
+ FeldmanVss :: <G1Projective >:: verify(
495
+ & new_pp,
496
+ j,
497
+ & new_secret_share,
498
+ & new_commitments[ j]
499
+ )
500
+ . is_ok( )
501
+ ) ;
502
+ }
503
+
504
+ // Reconstruct secret
505
+ let reconstructed_secret = FeldmanVss :: < G1Projective > :: reconstruct (
506
+ & new_pp,
507
+ ( 0 ..new_t) . map ( |i| ( i, new_shares[ i] ) ) ,
508
+ )
509
+ . unwrap ( ) ;
510
+
511
+ assert_eq ! ( reconstructed_secret, secret) ;
512
+ }
513
+
514
+ // Test success-path for identical (t,n) → (t',n') case
515
+ #[ test]
516
+ fn test_key_resharing_identical_params ( ) {
517
+ let rng = & mut test_rng ( ) ;
518
+
519
+ // Run 7 random trials (between 5-10)
520
+ for _ in 0 ..7 {
521
+ // Generate random (t,n) parameters
522
+ let n = rng. gen_range ( 5 ..12 ) ;
523
+ let t = rng. gen_range ( 2 ..n) ;
524
+ run_reshare_scenario ( t, n, t, n, rng) ;
525
+ }
526
+ }
527
+
528
+ // Test success-path for different (t,n) → (t',n') cases
529
+ #[ test]
530
+ fn test_key_resharing_different_threshold_committee_sizes ( ) {
531
+ let rng = & mut test_rng ( ) ;
532
+
533
+ // Run multiple random trials with different parameter combinations
534
+ for _ in 0 ..10 {
535
+ // Randomly choose (t,n) parameters for the original committee
536
+ let old_n = rng. gen_range ( 5 ..15 ) ;
537
+ let old_t = rng. gen_range ( 2 ..old_n) ;
538
+
539
+ // Randomly choose (t',n') parameters for the new committee with variations
540
+ // Sometimes larger, sometimes smaller than original
541
+ let new_n = match rng. gen_range ( 0 ..3 ) {
542
+ 0 => rng. gen_range ( 5 ..old_n) , // Smaller committee
543
+ 1 => rng. gen_range ( old_n..20 ) , // Larger committee
544
+ _ => rng. gen_range ( 5 ..20 ) , // Random size
545
+ } ;
546
+ let new_t = rng. gen_range ( 2 ..new_n) ;
547
+
548
+ run_reshare_scenario ( old_t, old_n, new_t, new_n, rng) ;
549
+ }
550
+ }
551
+
552
+ // Test specific edge cases for minimal thresholds and committee size limits
553
+ #[ test]
554
+ fn test_edge_case_minimal_thresholds ( ) {
555
+ let rng = & mut test_rng ( ) ;
556
+
557
+ // Edge case scenarios: (t, n) → (t', n')
558
+ let test_cases = vec ! [
559
+ ( ( 1 , 3 ) , ( 1 , 5 ) ) , // (t=1, n=3) → (t'=1, n'=5): minimal threshold expanding committee
560
+ ( ( 2 , 2 ) , ( 2 , 2 ) ) , // (t=2, n=2) → (t'=2, n'=2): minimal committee size (t=n)
561
+ ( ( 1 , 2 ) , ( 1 , 3 ) ) , // (t=1, n=2) → (t'=1, n'=3): minimal viable committee expanding
562
+ ( ( 1 , 4 ) , ( 1 , 2 ) ) , // (t=1, n=4) → (t'=1, n'=2): shrinking to minimal viable size
563
+ ( ( 2 , 3 ) , ( 1 , 4 ) ) , // (t=2, n=3) → (t'=1, n'=4): threshold reduction with expansion
564
+ ( ( 1 , 5 ) , ( 2 , 3 ) ) , // (t=1, n=5) → (t'=2, n'=3): threshold increase with shrinking
565
+ ] ;
566
+
567
+ for ( ( old_t, old_n) , ( new_t, new_n) ) in test_cases {
568
+ run_reshare_scenario ( old_t, old_n, new_t, new_n, rng) ;
569
+ }
570
+ }
300
571
}
0 commit comments