Skip to main content

confium_tc_keys/
key_mgmt_and_protocols.rs

1//! Key lifecycle, threshold CA, CRL, MPC, voting, shuffle, beacon, refresh.
2//!
3//! Production key management + advanced protocol primitives.
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::Mutex;
9
10// === Key Lifecycle Manager ===
11
12#[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        // Simulate rotation completion
92        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// === Threshold Certificate Authority ===
178
179#[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// === SPDZ-style MPC ===
294
295#[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
360// === Secure Sorting ===
361
362pub 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
376// === Threshold Statistics ===
377
378pub 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// === Commitment-Based Voting ===
422
423#[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// === Verifiable Shuffle ===
515
516#[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    // Fisher-Yates shuffle
527    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    // Generate proof (simplified: hash of permuted elements)
533    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    // Check: shuffled is a permutation of original (same multiset)
553    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    // Check proof consistency
561    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
570// === Threshold Random Beacon ===
571
572pub 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
634// === Multi-Party Key Refresh ===
635
636pub 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    // Key lifecycle
698    #[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    // Threshold CA
745    #[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    // SPDZ
798    #[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    // Secure sorting
832    #[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    // Threshold statistics
848    #[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    // Commit-reveal voting
869    #[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    // Verifiable shuffle
897    #[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); // almost certainly different order
909    }
910
911    // Threshold beacon
912    #[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    // Key refresh
937    #[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        // XOR of AA, BB, CC should be non-zero
947        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}