-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathtrait.rs
More file actions
908 lines (802 loc) · 32.7 KB
/
Copy pathtrait.rs
File metadata and controls
908 lines (802 loc) · 32.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
//! Crypto trait definitions
//!
//! This module defines the core cryptography abstractions that can be implemented
//! by various Curves.
use crate::error::{CryptoError, Result};
use std::collections::HashMap;
use std::fmt::Debug;
use zeroize::Zeroize;
/// Trait for types that can be serialized to bytes.
///
/// This provides a generic interface for crypto type serialization,
/// allowing implementations to use any serialization format (e.g., ark-serialize).
pub trait CryptoSerialize: Sized {
/// Serialize this value to bytes (compressed format preferred).
fn to_bytes(&self) -> Result<Vec<u8>>;
/// Returns the size in bytes of the serialized representation.
/// This can be used for pre-allocation and validation.
fn serialized_size() -> usize;
}
/// Trait for types that can be deserialized from bytes.
///
/// This provides a generic interface for crypto type deserialization,
/// allowing implementations to use any serialization format (e.g., ark-serialize).
pub trait CryptoDeserialize: Sized {
/// Deserialize a value from bytes.
fn from_bytes(bytes: &[u8]) -> Result<Self>;
}
/// A share distributed by one participant to another
#[derive(Clone, Debug)]
pub struct DistributedShare<ShareValue: Zeroize> {
pub from_id: u32,
pub to_id: u32,
pub value: ShareValue,
pub nonce: [u8; 16], // Nonce to prevent replay attacks
pub session_id: u128, // Session ID to prevent replay attacks
}
impl<ShareValue: Zeroize> Drop for DistributedShare<ShareValue> {
fn drop(&mut self) {
self.value.zeroize();
}
}
/// Private share containing an index and a scalar value
#[derive(Clone, Debug)]
pub struct PriShare<ShareValue: Zeroize> {
pub i: u32,
pub v: ShareValue,
}
impl<ShareValue: Zeroize> Drop for PriShare<ShareValue> {
fn drop(&mut self) {
self.v.zeroize();
}
}
/// Public share containing an index and a point value
#[derive(Clone, Debug)]
pub struct PubShare<PublicKey> {
pub i: u32,
pub v: PublicKey,
}
pub type SigShare<G> = PubShare<G>;
/// Distributed key share
#[derive(Clone, Debug)]
pub struct DistKeyShare<ShareValue: Zeroize> {
pub pri_share: PriShare<ShareValue>,
}
impl<ShareValue: Zeroize> Drop for DistKeyShare<ShareValue> {
fn drop(&mut self) {
self.pri_share.v.zeroize();
}
}
/// Secret structure
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct Secret {
pub enc_cmt: Vec<u8>, // rG - Schnorr commitment
pub encrypted_data: Vec<u8>, // AES-GCM encrypted data
pub nonce: Vec<u8>, // AES-GCM nonce (12 bytes)
}
/// Chaum-Pedersen NIZK proof that encryption was performed correctly.
/// Proves that enc_cmt = rG and shared_point = r*effective_pk use the same randomness r.
///
/// When capability derivation is used:
/// effective_pk = d * dkg_pk (where d = H(DERIVATION_DOMAIN || derivation))
/// Otherwise:
/// effective_pk = dkg_pk
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct EncryptionProof {
/// The shared point used for key derivation: r * effective_pk
pub shared_point: Vec<u8>,
/// Fiat-Shamir challenge
pub challenge: Vec<u8>,
/// Proof response (s = k + c*r)
pub response: Vec<u8>,
}
impl TryFrom<String> for EncryptionProof {
type Error = CryptoError;
fn try_from(string: String) -> Result<Self> {
serde_json::from_str(&string).map_err(|e| CryptoError::ParseError(e.to_string()))
}
}
impl TryFrom<EncryptionProof> for String {
type Error = CryptoError;
fn try_from(proof: EncryptionProof) -> Result<Self> {
serde_json::to_string(&proof).map_err(|e| CryptoError::ParseError(e.to_string()))
}
}
/// Re-encryption reply
#[derive(Clone, Debug)]
pub struct ReencryptReply<ShareValue: Zeroize, PublicKey> {
pub share: PubShare<PublicKey>,
pub challenge: ShareValue,
pub proof: ShareValue,
}
impl<ShareValue: Zeroize, PublicKey> Drop for ReencryptReply<ShareValue, PublicKey> {
fn drop(&mut self) {
self.challenge.zeroize();
self.proof.zeroize();
}
}
// ============================================================================
// Serialization implementations for generic structs
// ============================================================================
impl<ShareValue: CryptoSerialize + CryptoDeserialize + Zeroize> CryptoSerialize
for DistributedShare<ShareValue>
{
fn to_bytes(&self) -> Result<Vec<u8>> {
let value_bytes = zeroize::Zeroizing::new(self.value.to_bytes()?);
let value_len = value_bytes.len() as u32;
// Format: from_id (4) + to_id (4) + session_id (16) + nonce (16) + value_len (4) + value
let mut bytes = Vec::with_capacity(4 + 4 + 16 + 16 + 4 + value_bytes.len());
bytes.extend_from_slice(&self.from_id.to_le_bytes());
bytes.extend_from_slice(&self.to_id.to_le_bytes());
bytes.extend_from_slice(&self.session_id.to_le_bytes());
bytes.extend_from_slice(&self.nonce);
bytes.extend_from_slice(&value_len.to_le_bytes());
bytes.extend_from_slice(&value_bytes);
Ok(bytes)
}
fn serialized_size() -> usize {
// 4 + 4 + 16 + 16 + 4 + ShareValue::serialized_size()
44 + ShareValue::serialized_size()
}
}
impl<ShareValue: CryptoSerialize + CryptoDeserialize + Zeroize> CryptoDeserialize
for DistributedShare<ShareValue>
{
fn from_bytes(bytes: &[u8]) -> Result<Self> {
use crate::error::CryptoError;
if bytes.len() < 44 {
return Err(CryptoError::DKGError(
"DistributedShare bytes too short".to_string(),
));
}
let from_id = u32::from_le_bytes(
bytes[0..4]
.try_into()
.map_err(|_| CryptoError::DKGError("Invalid from_id bytes".to_string()))?,
);
let to_id = u32::from_le_bytes(
bytes[4..8]
.try_into()
.map_err(|_| CryptoError::DKGError("Invalid to_id bytes".to_string()))?,
);
let session_id = u128::from_le_bytes(
bytes[8..24]
.try_into()
.map_err(|_| CryptoError::DKGError("Invalid session_id bytes".to_string()))?,
);
let nonce: [u8; 16] = bytes[24..40]
.try_into()
.map_err(|_| CryptoError::DKGError("Invalid nonce bytes".to_string()))?;
let value_len = u32::from_le_bytes(
bytes[40..44]
.try_into()
.map_err(|_| CryptoError::DKGError("Invalid value_len bytes".to_string()))?,
) as usize;
let expected_len = 44 + value_len;
if bytes.len() < expected_len {
return Err(CryptoError::DKGError(
"DistributedShare bytes too short for value".to_string(),
));
}
if bytes.len() != expected_len {
return Err(CryptoError::DKGError(format!(
"DistributedShare bytes length mismatch: expected {}, got {}",
expected_len,
bytes.len()
)));
}
let value = ShareValue::from_bytes(&bytes[44..expected_len])?;
Ok(Self {
from_id,
to_id,
value,
nonce,
session_id,
})
}
}
impl<ShareValue: CryptoSerialize + CryptoDeserialize + Zeroize> CryptoSerialize
for PriShare<ShareValue>
{
fn to_bytes(&self) -> Result<Vec<u8>> {
let value_bytes = zeroize::Zeroizing::new(self.v.to_bytes()?);
// Format: i (4) + value
let mut bytes = Vec::with_capacity(4 + value_bytes.len());
bytes.extend_from_slice(&self.i.to_le_bytes());
bytes.extend_from_slice(&value_bytes);
Ok(bytes)
}
fn serialized_size() -> usize {
4 + ShareValue::serialized_size()
}
}
impl<ShareValue: CryptoSerialize + CryptoDeserialize + Zeroize> CryptoDeserialize
for PriShare<ShareValue>
{
fn from_bytes(bytes: &[u8]) -> Result<Self> {
use crate::error::CryptoError;
if bytes.len() < 4 {
return Err(CryptoError::DKGError(
"PriShare bytes too short".to_string(),
));
}
let i = u32::from_le_bytes(
bytes[0..4]
.try_into()
.map_err(|_| CryptoError::DKGError("Invalid PriShare index bytes".to_string()))?,
);
let v = ShareValue::from_bytes(&bytes[4..])?;
Ok(Self { i, v })
}
}
impl<PublicKey: CryptoSerialize + CryptoDeserialize> CryptoSerialize for PubShare<PublicKey> {
fn to_bytes(&self) -> Result<Vec<u8>> {
let value_bytes = self.v.to_bytes()?;
// Format: i (4) + value
let mut bytes = Vec::with_capacity(4 + value_bytes.len());
bytes.extend_from_slice(&self.i.to_le_bytes());
bytes.extend_from_slice(&value_bytes);
Ok(bytes)
}
fn serialized_size() -> usize {
4 + PublicKey::serialized_size()
}
}
impl<PublicKey: CryptoSerialize + CryptoDeserialize> CryptoDeserialize for PubShare<PublicKey> {
fn from_bytes(bytes: &[u8]) -> Result<Self> {
use crate::error::CryptoError;
if bytes.len() < 4 {
return Err(CryptoError::DKGError(
"PubShare bytes too short".to_string(),
));
}
let i = u32::from_le_bytes(
bytes[0..4]
.try_into()
.map_err(|_| CryptoError::DKGError("Invalid PubShare index bytes".to_string()))?,
);
let v = PublicKey::from_bytes(&bytes[4..])?;
Ok(Self { i, v })
}
}
impl<ShareValue: CryptoSerialize + CryptoDeserialize + Zeroize> CryptoSerialize
for DistKeyShare<ShareValue>
{
fn to_bytes(&self) -> Result<Vec<u8>> {
self.pri_share.to_bytes()
}
fn serialized_size() -> usize {
PriShare::<ShareValue>::serialized_size()
}
}
impl<ShareValue: CryptoSerialize + CryptoDeserialize + Zeroize> CryptoDeserialize
for DistKeyShare<ShareValue>
{
fn from_bytes(bytes: &[u8]) -> Result<Self> {
let pri_share = PriShare::from_bytes(bytes)?;
Ok(Self { pri_share })
}
}
impl<
ShareValue: CryptoSerialize + CryptoDeserialize + Zeroize,
PublicKey: CryptoSerialize + CryptoDeserialize,
> CryptoSerialize for ReencryptReply<ShareValue, PublicKey>
{
fn to_bytes(&self) -> Result<Vec<u8>> {
let share_bytes = self.share.to_bytes()?;
let challenge_bytes = zeroize::Zeroizing::new(self.challenge.to_bytes()?);
let proof_bytes = zeroize::Zeroizing::new(self.proof.to_bytes()?);
// Format: share_len (4) + share + challenge_len (4) + challenge + proof_len (4) + proof
let mut bytes =
Vec::with_capacity(12 + share_bytes.len() + challenge_bytes.len() + proof_bytes.len());
bytes.extend_from_slice(&(share_bytes.len() as u32).to_le_bytes());
bytes.extend_from_slice(&share_bytes);
bytes.extend_from_slice(&(challenge_bytes.len() as u32).to_le_bytes());
bytes.extend_from_slice(&challenge_bytes);
bytes.extend_from_slice(&(proof_bytes.len() as u32).to_le_bytes());
bytes.extend_from_slice(&proof_bytes);
Ok(bytes)
}
fn serialized_size() -> usize {
// 4 + PubShare size + 4 + ShareValue size + 4 + ShareValue size
12 + PubShare::<PublicKey>::serialized_size()
+ ShareValue::serialized_size()
+ ShareValue::serialized_size()
}
}
impl<
ShareValue: CryptoSerialize + CryptoDeserialize + Zeroize,
PublicKey: CryptoSerialize + CryptoDeserialize,
> CryptoDeserialize for ReencryptReply<ShareValue, PublicKey>
{
fn from_bytes(bytes: &[u8]) -> Result<Self> {
use crate::error::CryptoError;
if bytes.len() < 12 {
return Err(CryptoError::DKGError(
"ReencryptReply bytes too short".to_string(),
));
}
let mut offset = 0;
let share_len = u32::from_le_bytes(
bytes[offset..offset + 4]
.try_into()
.map_err(|_| CryptoError::DKGError("Invalid share_len bytes".to_string()))?,
) as usize;
offset += 4;
if bytes.len() < offset + share_len + 8 {
return Err(CryptoError::DKGError(
"ReencryptReply bytes too short for share".to_string(),
));
}
let share = PubShare::from_bytes(&bytes[offset..offset + share_len])?;
offset += share_len;
let challenge_len = u32::from_le_bytes(
bytes[offset..offset + 4]
.try_into()
.map_err(|_| CryptoError::DKGError("Invalid challenge_len bytes".to_string()))?,
) as usize;
offset += 4;
if bytes.len() < offset + challenge_len + 4 {
return Err(CryptoError::DKGError(
"ReencryptReply bytes too short for challenge".to_string(),
));
}
let challenge = ShareValue::from_bytes(&bytes[offset..offset + challenge_len])?;
offset += challenge_len;
let proof_len = u32::from_le_bytes(
bytes[offset..offset + 4]
.try_into()
.map_err(|_| CryptoError::DKGError("Invalid proof_len bytes".to_string()))?,
) as usize;
offset += 4;
let expected_len = offset + proof_len;
if bytes.len() < expected_len {
return Err(CryptoError::DKGError(
"ReencryptReply bytes too short for proof".to_string(),
));
}
if bytes.len() != expected_len {
return Err(CryptoError::DKGError(format!(
"ReencryptReply bytes length mismatch: expected {}, got {}",
expected_len,
bytes.len()
)));
}
let proof = ShareValue::from_bytes(&bytes[offset..expected_len])?;
Ok(Self {
share,
challenge,
proof,
})
}
}
// ============================================================================
// CryptoSerialize/CryptoDeserialize for unit type (used by non-interactive schemes)
// ============================================================================
impl CryptoSerialize for () {
fn to_bytes(&self) -> Result<Vec<u8>> {
Ok(Vec::new())
}
fn serialized_size() -> usize {
0
}
}
impl CryptoDeserialize for () {
fn from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.is_empty() {
Ok(())
} else {
Err(CryptoError::SerializationError(
ark_serialize::SerializationError::InvalidData,
))
}
}
}
pub trait PubPoly: Clone + Debug + Send + Sync + CryptoSerialize + CryptoDeserialize {
type PublicKey: CryptoSerialize + CryptoDeserialize;
/// Evaluate the public polynomial at index i
fn eval(&self, i: u32) -> Self::PublicKey;
}
pub trait PolynomialCommitment:
Clone + Debug + Send + Sync + CryptoSerialize + CryptoDeserialize
{
type PublicKey: CryptoSerialize + CryptoDeserialize;
type ShareValue: CryptoSerialize + CryptoDeserialize;
/// Evaluate the polynomial commitment at index i
fn eval(&self, i: u32) -> Self::PublicKey;
/// Verify a share against this commitment using constant-time comparison
fn verify_share(&self, share_id: u32, share_value: &Self::ShareValue) -> bool;
}
/// Role of a participant in the DKG protocol.
///
/// Only meaningful for resharing; use `Standard` for Fresh and Refresh modes.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DkgRole {
/// Symmetric participant in Fresh or Refresh — every node is equivalent.
Standard,
/// Old committee member only: generates a resharing polynomial and sends shares,
/// but does not receive shares or compute a new secret share.
Dealer,
/// New committee member only: receives shares from old dealers but does not
/// generate a polynomial.
Receiver,
/// Member of both old and new committees: generates a resharing polynomial
/// AND receives shares from other old dealers to compute a new share.
DealerReceiver,
}
/// Operational mode passed to `generate_polynomial`.
#[derive(Clone)]
pub enum DkgMode<F> {
/// Generate a new random distributed secret (standard DKG).
Fresh,
/// Rotate shares while keeping the same secret.
///
/// Each node contributes a polynomial with a zero constant term so the
/// aggregate delta is zero at `x = 0`. The output of `compute_secret_share`
/// is an additive delta — the node layer adds it to the existing share.
Refresh,
/// Redistribute the same secret to a (potentially different) committee.
///
/// Each old dealer builds a polynomial whose constant term is its unweighted
/// old share `sᵢ`. Receivers call `select_reshare_participants` with the
/// session-wide old-dealer subset before Phase 4; aggregation applies the
/// Lagrange weights for that selected subset.
Reshare {
/// This node's current secret share value.
old_share: F,
/// IDs of the old committee members participating in this reshare.
/// Must be at least a threshold-sized subset.
participating_ids: Vec<u32>,
/// Threshold for the new committee.
new_threshold: usize,
/// Total nodes in the new committee.
new_total_nodes: usize,
/// This node's index in the new committee (1-based), if it is also a
/// new-committee member (`DealerReceiver`). `None` for pure `Dealer` nodes.
/// Used so the crypto layer can validate incoming share `to_id` against the
/// new-committee index rather than the old-committee `self.id`.
new_node_id: Option<u32>,
},
}
/// Trait for DKG
pub trait Dkg: Send + Sync {
type ShareValue: CryptoSerialize + CryptoDeserialize + Clone + Send + Sync + Zeroize;
type PublicKey: CryptoSerialize + CryptoDeserialize + Clone;
type PubPoly: PubPoly<PublicKey = Self::PublicKey>;
type PolynomialCommitment: PolynomialCommitment<
PublicKey = Self::PublicKey,
ShareValue = Self::ShareValue,
>;
/// Initialize a new DKG node
///
/// # Arguments
/// * `id` - Unique identifier for this node (1-indexed)
/// * `threshold` - Minimum number of nodes needed to reconstruct (t)
/// * `total_nodes` - Total number of participating nodes (n)
/// * `session_id` - Session ID agreed upon by all nodes before starting DKG
/// * `role` - Participant role; use `DkgRole::Standard` for Fresh and Refresh
fn new(
id: u32,
threshold: usize,
total_nodes: usize,
session_id: u128,
role: DkgRole,
) -> Result<Box<Self>>
where
Self: Sized;
/// Phase 1: Generate and broadcast polynomial commitment
///
/// The constant term of the generated polynomial depends on `mode`:
/// - `Fresh`: uniformly random
/// - `Refresh`: zero (share rotation without changing the secret)
/// - `Reshare`: `old_share` (unweighted; receivers weight the selected subset)
///
/// Returns an error if called on a `Receiver`-role node.
fn generate_polynomial(&mut self, mode: DkgMode<Self::ShareValue>) -> Result<()>;
/// Select the old-dealer subset to use for reshare Phase 4 aggregation.
///
/// This must be called by reshare receivers before computing the final share,
/// aggregate public key, or public polynomial when fewer than all old dealers
/// participate. Implementations should canonicalize and validate the IDs.
fn select_reshare_participants(&mut self, participant_ids: Vec<u32>) -> Result<()>;
/// Phase 2: Generate shares for all other nodes
///
/// Returns a vector of shares to be sent to each node
fn generate_shares(&self) -> Result<Vec<DistributedShare<Self::ShareValue>>>;
/// Phase 3: Receive and verify a share from another node
fn receive_share(&mut self, share: DistributedShare<Self::ShareValue>) -> Result<()>;
/// Receive a commitment from another node
fn receive_commitment(
&mut self,
from_id: u32,
commitment: Self::PolynomialCommitment,
) -> Result<()>;
/// Phase 4: Compute the final secret share
///
/// Once all shares are received and verified, compute the final share
/// by summing all received shares (including own share)
fn compute_secret_share(&self) -> Result<PriShare<Self::ShareValue>>;
/// Compute the aggregate public key
///
/// The aggregate public key is the sum of all nodes' constant terms
/// in their polynomial commitments
fn compute_aggregate_public_key(&self) -> Result<Self::PublicKey>;
/// Get complaints about malicious nodes
fn get_complaints(&self) -> &HashMap<u32, Vec<u32>>;
/// Compute the public polynomial (sum of all commitments)
///
/// This is used for verification in the re-encryption protocol
fn compute_public_polynomial(&self) -> Result<Self::PubPoly>;
/// Get the node ID
fn node_id(&self) -> u32;
/// Get the threshold value
fn threshold(&self) -> usize;
/// Get the total number of nodes
fn total_nodes(&self) -> usize;
/// Get a reference to the polynomial commitment
fn commitment(&self) -> &Self::PolynomialCommitment;
/// Get the role of this node in the current DKG session.
fn role(&self) -> DkgRole;
/// Add two serialized public polynomials coefficient-wise and return the result.
///
/// Used in PSS refresh Phase 4 to compute the updated public polynomial:
/// new_pub_poly = old_pub_poly + refresh_delta_poly
///
/// Both slices must be produced by `PubPoly::to_bytes` and have equal length.
fn combine_pub_poly_bytes(a: &[u8], b: &[u8]) -> Result<Vec<u8>>
where
Self: Sized;
}
/// Trait for PRE
pub trait ThresholdDealer {
type DistKeyShare;
type Secret;
type PublicKey: CryptoSerialize + CryptoDeserialize + Clone;
type ShareValue: CryptoSerialize + CryptoDeserialize + Clone;
type ReencryptReply;
type PubPoly: PubPoly<PublicKey = Self::PublicKey>;
fn new() -> Self;
fn name() -> String;
/// Re-encrypt a secret share using the receiver's public key.
///
/// When derivation is provided, applies the derivation scalar to the share:
/// xnc_ski = d * ski * (xG + rG) where d = H(DERIVATION_DOMAIN || derivation)
///
/// Note: If the wrong derivation is provided, decryption will fail at the user
/// level (AES-GCM authentication failure). An attacker cannot brute-force the
/// correct derivation without the reader's private key.
///
/// Input:
/// dist_key_share - Private share of secret key of DKG.
/// scrt - The encrypted secret (contains enc_cmt = rG).
/// rdr_pk (xG) - Public key of the reader.
/// derivation - Optional capability derivation bytes.
///
/// Output:
/// xnc_ski (Ui) - Re-encrypted secret share (with derivation applied if provided).
/// chlgi (ei) - Random oracle challenge.
/// proofi (fi) - NIZK proof of re-encryption.
fn reencrypt(
&self,
dist_key_share: &Self::DistKeyShare,
scrt: &Self::Secret,
rdr_pk: &Self::PublicKey,
derivation: Option<&[u8]>,
) -> Result<Self::ReencryptReply>;
/// Verify a re-encryption proof.
///
/// When derivation is provided, verification uses d * dkg_cmt.eval(idx) as the
/// expected commitment for the share, matching the derivation applied during re-encryption.
///
/// Input:
/// rdr_pk (xG) - Public key of the reader.
/// dkg_cmt - Public polynomial commitment of DKG.
/// enc_cmt (rG) - Schnorr commit of encoded keys.
/// reply - Re-encryption reply containing share, challenge, and proof.
/// derivation - Optional capability derivation bytes (must match reencrypt).
fn verify(
&self,
rdr_pk: &Self::PublicKey,
dkg_cmt: &Self::PubPoly,
enc_cmt: &Self::PublicKey,
reply: &Self::ReencryptReply,
derivation: Option<&[u8]>,
) -> Result<()>;
/// Recover the re-encrypted commitment from shares
fn recover(
&self,
xnc_ski: &[PubShare<Self::PublicKey>],
t: usize,
n: usize,
) -> Result<Option<Self::PublicKey>>;
// These functions are not called by the node, but are required here intentionally.
// A complete curve implementation must provide the full client-side API (encrypt, verify, decrypt)
// so that clients can use any supported curve. Keeping them in one trait ensures the compiler
// enforces completeness — an implementer cannot ship a node-only impl and leave clients with a
// half-implemented curve.
/// Encrypt a secret using the aggregate public key of the DKG.
///
/// Input:
/// dkg_pk (sG) - Aggregate public key of the DKG.
/// data - Data to be encrypted.
/// derivation - Optional capability derivation bytes. When provided,
/// a scalar d = H(derivation) is derived and applied
/// multiplicatively: derived_pk = d * dkg_pk.
/// The shared_point becomes r * derived_pk = r*d*s*G.
/// metadata - Metadata Added to Chaum-Pedersen NIZK proof of correct encryption
///
///
/// Output:
/// enc_cmt - Schnorr commit (rG)
/// secret - Encrypted data with capability binding
/// proof - Chaum-Pedersen NIZK proof of correct encryption
fn encrypt_secret(
dkg_pk: &Self::PublicKey,
data: &[u8],
derivation: Option<&[u8]>,
metadata: Option<&[u8]>,
) -> Result<(Self::PublicKey, Self::Secret, EncryptionProof)>;
/// Verify that a secret was correctly encrypted to the given effective public key.
/// Uses Chaum-Pedersen NIZK proof to verify enc_cmt and shared_point use same randomness.
fn verify_encryption(
effective_pk: &Self::PublicKey,
enc_cmt: &Self::PublicKey,
proof: &EncryptionProof,
metadata: Option<&[u8]>,
) -> Result<()>;
/// Decrypt a secret using the reader's secret key.
///
/// Input:
/// effective_pk - The public key used for decryption:
/// - If derivation was used: derive_public_key(dkg_pk, derivation)
/// - Otherwise: dkg_pk (aggregate public key of DKG)
/// xnc_cmt - Re-encrypted commitment: d*(x+r)*sG (with derivation)
/// or (x+r)*sG (without derivation).
/// rdr_sk (x) - Secret key of the reader.
/// secret - The encrypted secret.
///
/// Output:
/// Decrypted data.
///
/// Note: When derivation was used during encryption/re-encryption:
/// - xnc_cmt = d * (x+r) * sG
/// - effective_pk = d * sG
/// - shared_point = xnc_cmt - x * effective_pk = d*r*sG
fn decrypt_secret(
effective_pk: &Self::PublicKey,
xnc_cmt: &Self::PublicKey,
rdr_sk: &Self::ShareValue,
secret: &Self::Secret,
) -> Result<Vec<u8>>;
/// Derive the effective public key from the DKG public key and derivation bytes.
///
/// Input:
/// dkg_pk - Aggregate public key of the DKG (sG).
/// derivation - Capability derivation bytes.
///
/// Output:
/// derived_pk = d * dkg_pk where d = H(DERIVATION_DOMAIN || derivation)
///
/// This is used by the decryptor to compute the effective_pk needed for decryption
/// when capability derivation was used during encryption.
fn derive_public_key(dkg_pk: &Self::PublicKey, derivation: &[u8]) -> Result<Self::PublicKey>;
/// Derive a symmetric AES key from an elliptic-curve point via HKDF-SHA256.
///
/// Used internally after re-encryption share recovery to derive the AES key
/// that decrypts the ciphertext. Exposed on the trait so generic tests can
/// verify determinism and point-distinctness without curve-specific imports.
fn derive_key_from_point(point: &Self::PublicKey) -> Result<[u8; 32]>;
/// Encode policy metadata fields into a 32-byte commitment for proof binding.
///
/// Both the encrypter and the PRE verification nodes must call this function
/// with the same inputs to produce a consistent metadata commitment to pass
/// to `encrypt_secret` and `verify_encryption`.
///
/// For decaf377 implementations, the output is the LE serialization of a
/// Poseidon377 Fq hash, enabling efficient in-circuit verification of the
/// raw policy fields without SHA256. For bls12-381, SHA256 is used.
fn encode_metadata(
policy_id: &str,
resource: &str,
permission: &str,
tier: Option<&str>,
timestamp: Option<u64>,
salt: Option<&str>,
) -> Vec<u8>;
}
pub trait ThresholdSigner {
/// Whether this scheme requires interactive nonce commitment rounds (FROST = true, BLS = false)
const INTERACTIVE: bool;
/// Scalar field (Fr for BLS12-381)
type ShareValue;
/// Signature type (usually G1Affine)
type Signature;
/// Public key type (usually G2Affine)
type PublicKey;
/// Public polynomial from DKG (commitments)
type PubPoly;
/// Distributed secret key share
type DistKeyShare;
/// Signature share type
type SigShare;
/// Nonce commitment broadcast in Round 1 (BLS: `()`, FROST: nonce pair)
type NonceCommitment: CryptoSerialize + CryptoDeserialize + Clone + Send + Sync;
/// Secret state held between Round 1 and Round 2 (BLS: `()`, FROST: secret nonces)
type SigningState: CryptoSerialize + CryptoDeserialize + Send + Sync;
/// Construct a new signer
fn new() -> Self;
/// Domain-separated name for the protocol
fn name() -> String;
/// Hash a message to the signing group (BLS only; FROST returns Err)
fn hash_message(&self, msg: &[u8]) -> Result<Self::Signature>;
/// Generate nonce commitments and secret signing state for Round 1
fn generate_nonces(
&self,
dist_key_share: &Self::DistKeyShare,
) -> Result<(Self::NonceCommitment, Self::SigningState)>;
/// Locally sign a message using a DKG share.
///
/// When `derivation` is `Some(bytes)`, each node multiplies its secret share by
/// `d = H(SIGN_DERIVATION_DOMAIN || derivation)` before signing, producing a signature
/// that verifies under the derived public key `d * agg_pk`.
///
/// When `metadata` is also provided, it is folded into the derivation scalar:
/// `d = H(SIGN_DERIVATION_DOMAIN || derivation || \x00 || len(metadata) || metadata)`.
/// The metadata is thus cryptographically bound to every signature share — the existing
/// verification equation (BLS pairing / FROST check) serves as the binding proof.
fn sign(
&self,
dist_key_share: &Self::DistKeyShare,
msg: &[u8],
pub_poly: &Self::PubPoly,
signing_state: Option<&Self::SigningState>,
all_commitments: &[(u32, Self::NonceCommitment)],
derivation: Option<&[u8]>,
metadata: Option<&[u8]>,
) -> Result<Self::SigShare>;
/// Verify a single signature share against the DKG commitments.
///
/// `derivation` and `metadata` must match the values passed to `sign`.
fn verify_share(
&self,
msg: &[u8],
pub_poly: &Self::PubPoly,
sig_share: &Self::SigShare,
all_commitments: &[(u32, Self::NonceCommitment)],
derivation: Option<&[u8]>,
metadata: Option<&[u8]>,
) -> Result<()>;
/// Recover a full signature from shares.
fn recover(
&self,
shares: &[Self::SigShare],
t: usize,
n: usize,
msg: &[u8],
all_commitments: &[(u32, Self::NonceCommitment)],
) -> Result<Option<Self::Signature>>;
/// Verify the final signature
fn verify(&self, pk: &Self::PublicKey, msg: &[u8], sig: &Self::Signature) -> Result<()>;
/// Encode signing policy fields into a metadata commitment for derivation binding.
///
/// Both the signer and any verifier must call this function with the same inputs
/// to produce the same bytes for `sign`, `verify_share`, and `derive_public_key`.
///
/// Uses SHA-256 with length-prefixed fields and a domain separator so the output
/// is collision-free across different `(policy_id, permission, resource)` pairs.
fn encode_metadata(policy_id: &str, resource: &str, permission: &str) -> Vec<u8>;
/// Derive a deterministic public key from the DKG aggregate public key.
///
/// Computes `pk' = d * dkg_pk` where:
/// - Without metadata: `d = H(SIGN_DERIVATION_DOMAIN || derivation)`
/// - With metadata: `d = H(SIGN_DERIVATION_DOMAIN || derivation || \x00 || len(metadata) || metadata)`
///
/// Signing with a share multiplied by `d` produces a signature that verifies under `pk'`.
///
/// Returns an error if `d` is zero (negligible probability).
fn derive_public_key(
dkg_pk: &Self::PublicKey,
derivation: &[u8],
metadata: Option<&[u8]>,
) -> Result<Self::PublicKey>;
}