1use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::Mutex;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
13#[serde(rename_all = "snake_case")]
14pub enum KeyState {
15 Generated,
16 Active,
17 Suspended,
18 Rotating,
19 Archived,
20 Destroyed,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct KeyRecord {
25 pub key_id: String,
26 pub quorum_id: String,
27 pub state: KeyState,
28 pub created_at: DateTime<Utc>,
29 pub rotated_at: Option<DateTime<Utc>>,
30 pub destroyed_at: Option<DateTime<Utc>>,
31 pub version: u32,
32}
33
34#[derive(Default)]
35pub struct KeyLifecycleManager {
36 keys: Mutex<HashMap<String, KeyRecord>>,
37 audit_log: Mutex<Vec<KeyAuditEntry>>,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct KeyAuditEntry {
42 pub key_id: String,
43 pub action: String,
44 pub timestamp: DateTime<Utc>,
45 pub actor: String,
46}
47
48impl KeyLifecycleManager {
49 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn generate(&self, key_id: &str, quorum_id: &str, actor: &str) -> Result<(), String> {
54 let mut keys = self.keys.lock().unwrap();
55 if keys.contains_key(key_id) {
56 return Err("key already exists".into());
57 }
58 keys.insert(
59 key_id.into(),
60 KeyRecord {
61 key_id: key_id.into(),
62 quorum_id: quorum_id.into(),
63 state: KeyState::Generated,
64 created_at: Utc::now(),
65 rotated_at: None,
66 destroyed_at: None,
67 version: 1,
68 },
69 );
70 self.audit(key_id, "generate", actor);
71 Ok(())
72 }
73
74 pub fn activate(&self, key_id: &str, actor: &str) -> Result<(), String> {
75 self.transition(key_id, KeyState::Active, actor, "activate")
76 }
77
78 pub fn suspend(&self, key_id: &str, actor: &str) -> Result<(), String> {
79 self.transition(key_id, KeyState::Suspended, actor, "suspend")
80 }
81
82 pub fn rotate(&self, key_id: &str, actor: &str) -> Result<(), String> {
83 let mut keys = self.keys.lock().unwrap();
84 let key = keys.get_mut(key_id).ok_or("key not found")?;
85 if key.state != KeyState::Active {
86 return Err("key not active".into());
87 }
88 key.state = KeyState::Rotating;
89 drop(keys);
90 self.audit(key_id, "rotate_start", actor);
91 let mut keys = self.keys.lock().unwrap();
93 let key = keys.get_mut(key_id).ok_or("key not found")?;
94 key.state = KeyState::Active;
95 key.version += 1;
96 key.rotated_at = Some(Utc::now());
97 drop(keys);
98 self.audit(key_id, "rotate_complete", actor);
99 Ok(())
100 }
101
102 pub fn archive(&self, key_id: &str, actor: &str) -> Result<(), String> {
103 self.transition(key_id, KeyState::Archived, actor, "archive")
104 }
105
106 pub fn destroy(&self, key_id: &str, actor: &str) -> Result<(), String> {
107 let mut keys = self.keys.lock().unwrap();
108 let key = keys.get_mut(key_id).ok_or("key not found")?;
109 if key.state == KeyState::Destroyed {
110 return Err("already destroyed".into());
111 }
112 key.state = KeyState::Destroyed;
113 key.destroyed_at = Some(Utc::now());
114 drop(keys);
115 self.audit(key_id, "destroy", actor);
116 Ok(())
117 }
118
119 pub fn get(&self, key_id: &str) -> Option<KeyRecord> {
120 self.keys.lock().unwrap().get(key_id).cloned()
121 }
122
123 pub fn state(&self, key_id: &str) -> Option<KeyState> {
124 self.keys
125 .lock()
126 .unwrap()
127 .get(key_id)
128 .map(|k| k.state.clone())
129 }
130
131 pub fn version(&self, key_id: &str) -> Option<u32> {
132 self.keys.lock().unwrap().get(key_id).map(|k| k.version)
133 }
134
135 pub fn audit_log(&self) -> Vec<KeyAuditEntry> {
136 self.audit_log.lock().unwrap().clone()
137 }
138
139 pub fn count_by_state(&self, state: &KeyState) -> usize {
140 self.keys
141 .lock()
142 .unwrap()
143 .values()
144 .filter(|k| &k.state == state)
145 .count()
146 }
147
148 pub fn key_count(&self) -> usize {
149 self.keys.lock().unwrap().len()
150 }
151
152 fn transition(
153 &self,
154 key_id: &str,
155 new_state: KeyState,
156 actor: &str,
157 action: &str,
158 ) -> Result<(), String> {
159 let mut keys = self.keys.lock().unwrap();
160 let key = keys.get_mut(key_id).ok_or("key not found")?;
161 key.state = new_state;
162 drop(keys);
163 self.audit(key_id, action, actor);
164 Ok(())
165 }
166
167 fn audit(&self, key_id: &str, action: &str, actor: &str) {
168 self.audit_log.lock().unwrap().push(KeyAuditEntry {
169 key_id: key_id.into(),
170 action: action.into(),
171 timestamp: Utc::now(),
172 actor: actor.into(),
173 });
174 }
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct CertificateRequest {
181 pub common_name: String,
182 pub public_key_hex: String,
183 pub requested_by: String,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct IssuedCertificate {
188 pub serial: u64,
189 pub common_name: String,
190 pub public_key_hex: String,
191 pub issued_at: DateTime<Utc>,
192 pub expires_at: DateTime<Utc>,
193 pub issuer: String,
194 pub signature_hex: String,
195 pub revoked: bool,
196}
197
198#[derive(Default)]
199pub struct ThresholdCa {
200 certs: Mutex<Vec<IssuedCertificate>>,
201 serial: Mutex<u64>,
202 ca_name: String,
203}
204
205impl ThresholdCa {
206 pub fn new(ca_name: &str) -> Self {
207 Self {
208 certs: Mutex::new(Vec::new()),
209 serial: Mutex::new(1),
210 ca_name: ca_name.into(),
211 }
212 }
213
214 pub fn issue(
215 &self,
216 csr: CertificateRequest,
217 validity_days: u32,
218 ) -> Result<IssuedCertificate, String> {
219 let serial = {
220 let mut s = self.serial.lock().unwrap();
221 let v = *s;
222 *s += 1;
223 v
224 };
225 let now = Utc::now();
226 let cert = IssuedCertificate {
227 serial,
228 common_name: csr.common_name.clone(),
229 public_key_hex: csr.public_key_hex,
230 issued_at: now,
231 expires_at: now + chrono::Duration::days(validity_days as i64),
232 issuer: self.ca_name.clone(),
233 signature_hex: format!("threshold-sig-{serial}"),
234 revoked: false,
235 };
236 self.certs.lock().unwrap().push(cert.clone());
237 Ok(cert)
238 }
239
240 pub fn revoke(&self, serial: u64) -> Result<(), String> {
241 let mut certs = self.certs.lock().unwrap();
242 let cert = certs
243 .iter_mut()
244 .find(|c| c.serial == serial)
245 .ok_or("cert not found")?;
246 cert.revoked = true;
247 Ok(())
248 }
249
250 pub fn get(&self, serial: u64) -> Option<IssuedCertificate> {
251 self.certs
252 .lock()
253 .unwrap()
254 .iter()
255 .find(|c| c.serial == serial)
256 .cloned()
257 }
258
259 pub fn is_valid(&self, serial: u64) -> bool {
260 self.certs
261 .lock()
262 .unwrap()
263 .iter()
264 .find(|c| c.serial == serial)
265 .map(|c| !c.revoked && c.expires_at > Utc::now())
266 .unwrap_or(false)
267 }
268
269 pub fn cert_count(&self) -> usize {
270 self.certs.lock().unwrap().len()
271 }
272
273 pub fn revoked_count(&self) -> usize {
274 self.certs
275 .lock()
276 .unwrap()
277 .iter()
278 .filter(|c| c.revoked)
279 .count()
280 }
281
282 pub fn generate_crl(&self) -> Vec<u64> {
283 self.certs
284 .lock()
285 .unwrap()
286 .iter()
287 .filter(|c| c.revoked)
288 .map(|c| c.serial)
289 .collect()
290 }
291}
292
293#[derive(Debug, Clone)]
296pub struct SpdzShare {
297 pub value: i64,
298 pub mac: i64,
299}
300
301pub struct SpdzParty {
302 pub id: u32,
303 pub mac_key: i64,
304}
305
306impl SpdzParty {
307 pub fn new(id: u32, mac_key: i64) -> Self {
308 Self { id, mac_key }
309 }
310
311 pub fn share(&self, secret: i64, n_parties: u32) -> Vec<SpdzShare> {
312 use rand_core::{OsRng, RngCore};
313 let mut shares = Vec::with_capacity(n_parties as usize);
314 let mut sum = 0i64;
315 for _i in 0..(n_parties - 1) {
316 let val = (OsRng.next_u32() as i64) % 10000;
317 sum += val;
318 shares.push(SpdzShare {
319 value: val,
320 mac: val * self.mac_key,
321 });
322 }
323 let last = secret - sum;
324 shares.push(SpdzShare {
325 value: last,
326 mac: last * self.mac_key,
327 });
328 shares
329 }
330
331 pub fn verify_mac(&self, share: &SpdzShare) -> bool {
332 share.mac == share.value * self.mac_key
333 }
334
335 pub fn open(shares: &[SpdzShare]) -> i64 {
336 shares.iter().map(|s| s.value).sum()
337 }
338
339 pub fn add(a: &[SpdzShare], b: &[SpdzShare]) -> Vec<SpdzShare> {
340 a.iter()
341 .zip(b.iter())
342 .map(|(x, y)| SpdzShare {
343 value: x.value + y.value,
344 mac: x.mac + y.mac,
345 })
346 .collect()
347 }
348
349 pub fn scalar_mul(shares: &[SpdzShare], c: i64) -> Vec<SpdzShare> {
350 shares
351 .iter()
352 .map(|s| SpdzShare {
353 value: s.value * c,
354 mac: s.mac * c,
355 })
356 .collect()
357 }
358}
359
360pub fn secure_sort(values: &[i64]) -> Vec<i64> {
363 let mut sorted = values.to_vec();
364 sorted.sort();
365 sorted
366}
367
368pub fn secure_sort_with_permutation(values: &[i64]) -> (Vec<i64>, Vec<usize>) {
369 let mut indexed: Vec<(usize, i64)> = values.iter().enumerate().map(|(i, &v)| (i, v)).collect();
370 indexed.sort_by_key(|&(_, v)| v);
371 let sorted = indexed.iter().map(|&(_, v)| v).collect();
372 let perm = indexed.iter().map(|&(i, _)| i).collect();
373 (sorted, perm)
374}
375
376pub fn threshold_sum(shares: &[i64]) -> i64 {
379 shares.iter().sum()
380}
381pub fn threshold_mean(shares: &[i64]) -> f64 {
382 if shares.is_empty() {
383 return 0.0;
384 }
385 threshold_sum(shares) as f64 / shares.len() as f64
386}
387pub fn threshold_variance(shares: &[i64]) -> f64 {
388 if shares.len() < 2 {
389 return 0.0;
390 }
391 let mean = threshold_mean(shares);
392 shares
393 .iter()
394 .map(|&v| (v as f64 - mean).powi(2))
395 .sum::<f64>()
396 / (shares.len() - 1) as f64
397}
398pub fn threshold_count(shares: &[i64]) -> usize {
399 shares.len()
400}
401pub fn threshold_min(shares: &[i64]) -> Option<i64> {
402 shares.iter().copied().min()
403}
404pub fn threshold_max(shares: &[i64]) -> Option<i64> {
405 shares.iter().copied().max()
406}
407pub fn threshold_median(shares: &[i64]) -> Option<f64> {
408 if shares.is_empty() {
409 return None;
410 }
411 let mut sorted = shares.to_vec();
412 sorted.sort();
413 let mid = sorted.len() / 2;
414 if sorted.len() % 2 == 0 {
415 Some((sorted[mid - 1] as f64 + sorted[mid] as f64) / 2.0)
416 } else {
417 Some(sorted[mid] as f64)
418 }
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct VoteCommitment {
425 pub voter: String,
426 pub commitment_hex: String,
427}
428
429#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct VoteReveal {
431 pub voter: String,
432 pub vote: String,
433 pub nonce_hex: String,
434}
435
436pub struct CommitRevealVote {
437 commitments: Mutex<Vec<VoteCommitment>>,
438 reveals: Mutex<Vec<VoteReveal>>,
439 committed: Mutex<bool>,
440}
441
442impl CommitRevealVote {
443 pub fn new() -> Self {
444 Self {
445 commitments: Mutex::new(Vec::new()),
446 reveals: Mutex::new(Vec::new()),
447 committed: Mutex::new(false),
448 }
449 }
450
451 pub fn commit(&self, voter: &str, commitment_hex: &str) -> Result<(), String> {
452 if *self.committed.lock().unwrap() {
453 return Err("commit phase ended".into());
454 }
455 let commitments = self.commitments.lock().unwrap();
456 if commitments.iter().any(|c| c.voter == voter) {
457 return Err("already committed".into());
458 }
459 drop(commitments);
460 self.commitments.lock().unwrap().push(VoteCommitment {
461 voter: voter.into(),
462 commitment_hex: commitment_hex.into(),
463 });
464 Ok(())
465 }
466
467 pub fn end_commit_phase(&self) {
468 *self.committed.lock().unwrap() = true;
469 }
470
471 pub fn reveal(&self, voter: &str, vote: &str, nonce_hex: &str) -> Result<(), String> {
472 if !*self.committed.lock().unwrap() {
473 return Err("commit phase not ended".into());
474 }
475 let commitments = self.commitments.lock().unwrap();
476 if !commitments.iter().any(|c| c.voter == voter) {
477 return Err("not committed".into());
478 }
479 let reveals = self.reveals.lock().unwrap();
480 if reveals.iter().any(|r| r.voter == voter) {
481 return Err("already revealed".into());
482 }
483 drop(reveals);
484 self.reveals.lock().unwrap().push(VoteReveal {
485 voter: voter.into(),
486 vote: vote.into(),
487 nonce_hex: nonce_hex.into(),
488 });
489 Ok(())
490 }
491
492 pub fn tally(&self) -> HashMap<String, usize> {
493 let mut counts = HashMap::new();
494 for reveal in self.reveals.lock().unwrap().iter() {
495 *counts.entry(reveal.vote.clone()).or_insert(0) += 1;
496 }
497 counts
498 }
499
500 pub fn voter_count(&self) -> usize {
501 self.commitments.lock().unwrap().len()
502 }
503 pub fn reveal_count(&self) -> usize {
504 self.reveals.lock().unwrap().len()
505 }
506}
507
508impl Default for CommitRevealVote {
509 fn default() -> Self {
510 Self::new()
511 }
512}
513
514#[derive(Debug, Clone)]
517pub struct ShuffleProof {
518 pub permuted: Vec<[u8; 32]>,
519 pub proof_hex: String,
520}
521
522pub fn shuffle(elements: &[[u8; 32]]) -> (Vec<[u8; 32]>, ShuffleProof, Vec<usize>) {
523 use rand_core::{OsRng, RngCore};
524 let n = elements.len();
525 let mut perm: Vec<usize> = (0..n).collect();
526 for i in (1..n).rev() {
528 let j = (OsRng.next_u32() as usize) % (i + 1);
529 perm.swap(i, j);
530 }
531 let permuted: Vec<[u8; 32]> = perm.iter().map(|&i| elements[i]).collect();
532 use sha2::{Digest, Sha256};
534 let mut hasher = Sha256::new();
535 hasher.update(b"shuffle-proof");
536 for e in &permuted {
537 hasher.update(e);
538 }
539 let proof = hex::encode(hasher.finalize());
540 let original: Vec<[u8; 32]> = permuted.clone();
541 (
542 permuted,
543 ShuffleProof {
544 permuted: original,
545 proof_hex: proof,
546 },
547 perm,
548 )
549}
550
551pub fn verify_shuffle(original: &[[u8; 32]], shuffled: &[[u8; 32]], proof: &ShuffleProof) -> bool {
552 let mut orig_sorted = original.to_vec();
554 let mut shuf_sorted = shuffled.to_vec();
555 orig_sorted.sort();
556 shuf_sorted.sort();
557 if orig_sorted != shuf_sorted {
558 return false;
559 }
560 use sha2::{Digest, Sha256};
562 let mut hasher = Sha256::new();
563 hasher.update(b"shuffle-proof");
564 for e in shuffled {
565 hasher.update(e);
566 }
567 hex::encode(hasher.finalize()) == proof.proof_hex
568}
569
570pub struct ThresholdBeacon {
573 round: Mutex<u64>,
574 shares: Mutex<HashMap<u64, Vec<Vec<u8>>>>,
575 threshold: u32,
576}
577
578impl ThresholdBeacon {
579 pub fn new(threshold: u32) -> Self {
580 Self {
581 round: Mutex::new(0),
582 shares: Mutex::new(HashMap::new()),
583 threshold,
584 }
585 }
586
587 pub fn next_round(&self) -> u64 {
588 let mut r = self.round.lock().unwrap();
589 *r += 1;
590 self.shares.lock().unwrap().insert(*r, Vec::new());
591 *r
592 }
593
594 pub fn submit_share(&self, round: u64, share: Vec<u8>) -> Result<(), String> {
595 let mut shares = self.shares.lock().unwrap();
596 let round_shares = shares.get_mut(&round).ok_or("invalid round")?;
597 round_shares.push(share);
598 Ok(())
599 }
600
601 pub fn is_ready(&self, round: u64) -> bool {
602 self.shares
603 .lock()
604 .unwrap()
605 .get(&round)
606 .map(|s| s.len() >= self.threshold as usize)
607 .unwrap_or(false)
608 }
609
610 pub fn produce_output(&self, round: u64) -> Option<[u8; 32]> {
611 if !self.is_ready(round) {
612 return None;
613 }
614 let shares = self.shares.lock().unwrap();
615 let round_shares = shares.get(&round)?;
616 use sha2::{Digest, Sha256};
617 let mut hasher = Sha256::new();
618 hasher.update(b"beacon");
619 hasher.update(round.to_be_bytes());
620 for s in round_shares {
621 hasher.update(s);
622 }
623 let result = hasher.finalize();
624 let mut output = [0u8; 32];
625 output.copy_from_slice(&result);
626 Some(output)
627 }
628
629 pub fn current_round(&self) -> u64 {
630 *self.round.lock().unwrap()
631 }
632}
633
634pub struct KeyRefreshProtocol {
637 threshold: u32,
638 party_count: u32,
639 contributions: Mutex<HashMap<u32, Vec<u8>>>,
640}
641
642impl KeyRefreshProtocol {
643 pub fn new(threshold: u32, party_count: u32) -> Self {
644 Self {
645 threshold,
646 party_count,
647 contributions: Mutex::new(HashMap::new()),
648 }
649 }
650
651 pub fn submit_contribution(&self, party_idx: u32, contribution: Vec<u8>) -> Result<(), String> {
652 if party_idx == 0 || party_idx > self.party_count {
653 return Err("invalid party index".into());
654 }
655 let mut contributions = self.contributions.lock().unwrap();
656 if contributions.contains_key(&party_idx) {
657 return Err("duplicate contribution".into());
658 }
659 contributions.insert(party_idx, contribution);
660 Ok(())
661 }
662
663 pub fn is_complete(&self) -> bool {
664 self.contributions.lock().unwrap().len() == self.party_count as usize
665 }
666
667 pub fn compute_refresh_delta(&self) -> Option<Vec<u8>> {
668 if !self.is_complete() {
669 return None;
670 }
671 let contributions = self.contributions.lock().unwrap();
672 let max_len = contributions.values().map(|c| c.len()).max()?;
673 let mut delta = vec![0u8; max_len];
674 for contrib in contributions.values() {
675 for (i, &b) in contrib.iter().enumerate() {
676 delta[i] ^= b;
677 }
678 }
679 Some(delta)
680 }
681
682 pub fn missing_parties(&self) -> Vec<u32> {
683 (1..=self.party_count)
684 .filter(|i| !self.contributions.lock().unwrap().contains_key(i))
685 .collect()
686 }
687
688 pub fn contribution_count(&self) -> usize {
689 self.contributions.lock().unwrap().len()
690 }
691}
692
693#[cfg(test)]
694mod tests {
695 use super::*;
696
697 #[test]
699 fn key_generate_and_activate() {
700 let mgr = KeyLifecycleManager::new();
701 mgr.generate("k1", "q1", "admin").unwrap();
702 assert_eq!(mgr.state("k1"), Some(KeyState::Generated));
703 mgr.activate("k1", "admin").unwrap();
704 assert_eq!(mgr.state("k1"), Some(KeyState::Active));
705 }
706
707 #[test]
708 fn key_rotate_increments_version() {
709 let mgr = KeyLifecycleManager::new();
710 mgr.generate("k1", "q1", "admin").unwrap();
711 mgr.activate("k1", "admin").unwrap();
712 assert_eq!(mgr.version("k1"), Some(1));
713 mgr.rotate("k1", "admin").unwrap();
714 assert_eq!(mgr.version("k1"), Some(2));
715 }
716
717 #[test]
718 fn key_destroy_final() {
719 let mgr = KeyLifecycleManager::new();
720 mgr.generate("k1", "q1", "admin").unwrap();
721 mgr.destroy("k1", "admin").unwrap();
722 assert_eq!(mgr.state("k1"), Some(KeyState::Destroyed));
723 assert!(mgr.destroy("k1", "admin").is_err());
724 }
725
726 #[test]
727 fn key_audit_trail() {
728 let mgr = KeyLifecycleManager::new();
729 mgr.generate("k1", "q1", "alice").unwrap();
730 mgr.activate("k1", "alice").unwrap();
731 assert_eq!(mgr.audit_log().len(), 2);
732 }
733
734 #[test]
735 fn key_count_by_state() {
736 let mgr = KeyLifecycleManager::new();
737 mgr.generate("k1", "q1", "a").unwrap();
738 mgr.generate("k2", "q1", "a").unwrap();
739 mgr.activate("k1", "a").unwrap();
740 assert_eq!(mgr.count_by_state(&KeyState::Generated), 1);
741 assert_eq!(mgr.count_by_state(&KeyState::Active), 1);
742 }
743
744 #[test]
746 fn ca_issue_cert() {
747 let ca = ThresholdCa::new("Confium CA");
748 let cert = ca
749 .issue(
750 CertificateRequest {
751 common_name: "example.com".into(),
752 public_key_hex: "abcd".into(),
753 requested_by: "admin".into(),
754 },
755 365,
756 )
757 .unwrap();
758 assert_eq!(cert.serial, 1);
759 assert!(ca.is_valid(1));
760 }
761
762 #[test]
763 fn ca_revoke_cert() {
764 let ca = ThresholdCa::new("CA");
765 ca.issue(
766 CertificateRequest {
767 common_name: "x".into(),
768 public_key_hex: "pk".into(),
769 requested_by: "a".into(),
770 },
771 30,
772 )
773 .unwrap();
774 ca.revoke(1).unwrap();
775 assert!(!ca.is_valid(1));
776 }
777
778 #[test]
779 fn ca_generate_crl() {
780 let ca = ThresholdCa::new("CA");
781 for i in 1..=3 {
782 ca.issue(
783 CertificateRequest {
784 common_name: format!("cn{i}"),
785 public_key_hex: "pk".into(),
786 requested_by: "a".into(),
787 },
788 30,
789 )
790 .unwrap();
791 }
792 ca.revoke(2).unwrap();
793 let crl = ca.generate_crl();
794 assert_eq!(crl, vec![2]);
795 }
796
797 #[test]
799 fn spdz_share_and_open() {
800 let party = SpdzParty::new(1, 42);
801 let shares = party.share(100, 3);
802 assert_eq!(SpdzParty::open(&shares), 100);
803 }
804
805 #[test]
806 fn spdz_mac_verification() {
807 let party = SpdzParty::new(1, 42);
808 let shares = party.share(100, 3);
809 for s in &shares {
810 assert!(party.verify_mac(s));
811 }
812 }
813
814 #[test]
815 fn spdz_homomorphic_add() {
816 let party = SpdzParty::new(1, 42);
817 let s1 = party.share(30, 3);
818 let s2 = party.share(70, 3);
819 let sum = SpdzParty::add(&s1, &s2);
820 assert_eq!(SpdzParty::open(&sum), 100);
821 }
822
823 #[test]
824 fn spdz_scalar_mul() {
825 let party = SpdzParty::new(1, 42);
826 let shares = party.share(50, 3);
827 let scaled = SpdzParty::scalar_mul(&shares, 2);
828 assert_eq!(SpdzParty::open(&scaled), 100);
829 }
830
831 #[test]
833 fn secure_sort_works() {
834 assert_eq!(
835 secure_sort(&[3, 1, 4, 1, 5, 9, 2, 6]),
836 vec![1, 1, 2, 3, 4, 5, 6, 9]
837 );
838 }
839
840 #[test]
841 fn secure_sort_with_perm() {
842 let (sorted, perm) = secure_sort_with_permutation(&[3, 1, 2]);
843 assert_eq!(sorted, vec![1, 2, 3]);
844 assert_eq!(perm, vec![1, 2, 0]);
845 }
846
847 #[test]
849 fn stats_sum_mean() {
850 let data = vec![1, 2, 3, 4, 5];
851 assert_eq!(threshold_sum(&data), 15);
852 assert!((threshold_mean(&data) - 3.0).abs() < 0.01);
853 }
854
855 #[test]
856 fn stats_variance() {
857 let data = vec![2, 4, 4, 4, 5, 5, 7, 9];
858 let var = threshold_variance(&data);
859 assert!(var > 0.0);
860 }
861
862 #[test]
863 fn stats_median() {
864 assert_eq!(threshold_median(&[1, 3, 5]), Some(3.0));
865 assert_eq!(threshold_median(&[1, 2, 3, 4]), Some(2.5));
866 }
867
868 #[test]
870 fn voting_full_cycle() {
871 let vote = CommitRevealVote::new();
872 vote.commit("alice", "hash1").unwrap();
873 vote.commit("bob", "hash2").unwrap();
874 vote.end_commit_phase();
875 vote.reveal("alice", "yes", "nonce1").unwrap();
876 vote.reveal("bob", "no", "nonce2").unwrap();
877 let tally = vote.tally();
878 assert_eq!(tally.get("yes"), Some(&1));
879 assert_eq!(tally.get("no"), Some(&1));
880 }
881
882 #[test]
883 fn voting_cannot_reveal_before_commit_ends() {
884 let vote = CommitRevealVote::new();
885 vote.commit("alice", "h").unwrap();
886 assert!(vote.reveal("alice", "yes", "n").is_err());
887 }
888
889 #[test]
890 fn voting_double_commit_rejected() {
891 let vote = CommitRevealVote::new();
892 vote.commit("alice", "h1").unwrap();
893 assert!(vote.commit("alice", "h2").is_err());
894 }
895
896 #[test]
898 fn shuffle_and_verify() {
899 let elements: Vec<[u8; 32]> = (0..5).map(|i| [i as u8; 32]).collect();
900 let (shuffled, proof, _perm) = shuffle(&elements);
901 assert!(verify_shuffle(&elements, &shuffled, &proof));
902 }
903
904 #[test]
905 fn shuffle_changes_order() {
906 let elements: Vec<[u8; 32]> = (0..10).map(|i| [i as u8; 32]).collect();
907 let (shuffled, _, _) = shuffle(&elements);
908 assert_ne!(shuffled, elements); }
910
911 #[test]
913 fn beacon_round_and_output() {
914 let beacon = ThresholdBeacon::new(3);
915 let round = beacon.next_round();
916 beacon.submit_share(round, vec![1]).unwrap();
917 beacon.submit_share(round, vec![2]).unwrap();
918 assert!(!beacon.is_ready(round));
919 beacon.submit_share(round, vec![3]).unwrap();
920 assert!(beacon.is_ready(round));
921 let output = beacon.produce_output(round).unwrap();
922 assert_eq!(output.len(), 32);
923 }
924
925 #[test]
926 fn beacon_deterministic_output() {
927 let beacon = ThresholdBeacon::new(2);
928 let r = beacon.next_round();
929 beacon.submit_share(r, vec![0xAA]).unwrap();
930 beacon.submit_share(r, vec![0xBB]).unwrap();
931 let o1 = beacon.produce_output(r).unwrap();
932 let o2 = beacon.produce_output(r).unwrap();
933 assert_eq!(o1, o2);
934 }
935
936 #[test]
938 fn refresh_completes() {
939 let refresh = KeyRefreshProtocol::new(2, 3);
940 refresh.submit_contribution(1, vec![0xAA; 32]).unwrap();
941 refresh.submit_contribution(2, vec![0xBB; 32]).unwrap();
942 refresh.submit_contribution(3, vec![0xCC; 32]).unwrap();
943 assert!(refresh.is_complete());
944 let delta = refresh.compute_refresh_delta().unwrap();
945 assert_eq!(delta.len(), 32);
946 assert!(delta.iter().any(|&b| b != 0));
948 }
949
950 #[test]
951 fn refresh_incomplete_returns_none() {
952 let refresh = KeyRefreshProtocol::new(2, 3);
953 refresh.submit_contribution(1, vec![1]).unwrap();
954 assert!(!refresh.is_complete());
955 assert!(refresh.compute_refresh_delta().is_none());
956 }
957
958 #[test]
959 fn refresh_missing_parties() {
960 let refresh = KeyRefreshProtocol::new(2, 5);
961 refresh.submit_contribution(1, vec![1]).unwrap();
962 refresh.submit_contribution(3, vec![3]).unwrap();
963 assert_eq!(refresh.missing_parties(), vec![2, 4, 5]);
964 }
965}