Skip to main content

saorsa_core/threshold/
mod.rs

1// Copyright 2024 Saorsa Labs Limited
2//
3// This software is dual-licensed under:
4// - GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later)
5// - Commercial License
6//
7// For AGPL-3.0 license, see LICENSE-AGPL-3.0
8// For commercial licensing, contact: david@saorsalabs.com
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under these licenses is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
14//! Threshold cryptography module
15//!
16//! Implements FROST (Flexible Round-Optimized Schnorr Threshold) signatures
17//! and dynamic group management with Byzantine fault tolerance.
18
19pub mod dkg;
20pub mod frost;
21pub mod group;
22
23pub use self::group::*;
24
25use crate::quantum_crypto::types::*;
26use serde::{Deserialize, Serialize};
27use std::collections::HashMap;
28use std::time::SystemTime;
29use thiserror::Error;
30
31/// Threshold cryptography errors
32#[derive(Debug, Error)]
33pub enum ThresholdError {
34    #[error("Invalid threshold parameters: {0}")]
35    InvalidParameters(String),
36
37    #[error("Insufficient participants: need {required}, have {available}")]
38    InsufficientParticipants { required: u16, available: u16 },
39
40    #[error("Invalid share: {0}")]
41    InvalidShare(String),
42
43    #[error("DKG ceremony failed: {0}")]
44    DkgFailed(String),
45
46    #[error("Signature aggregation failed: {0}")]
47    AggregationFailed(String),
48
49    #[error("Group operation failed: {0}")]
50    GroupOperationFailed(String),
51
52    #[error("Consensus failed: {0}")]
53    ConsensusFailed(String),
54
55    #[error("Participant not found: {0}")]
56    ParticipantNotFound(ParticipantId),
57
58    #[error("Unauthorized operation: {0}")]
59    Unauthorized(String),
60}
61
62/// Result type for threshold operations
63pub type Result<T> = std::result::Result<T, ThresholdError>;
64
65/// Threshold signature type (placeholder)
66pub type ThresholdSignature = Vec<u8>;
67
68/// Threshold group with dynamic membership
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct ThresholdGroup {
71    /// Unique group identifier
72    pub group_id: GroupId,
73
74    /// Current threshold (t in t-of-n)
75    pub threshold: u16,
76
77    /// Total participants (n in t-of-n)
78    pub participants: u16,
79
80    /// FROST group public key
81    pub frost_group_key: FrostGroupPublicKey,
82
83    /// Active participants with their shares
84    pub active_participants: Vec<ParticipantInfo>,
85
86    /// Participants being added
87    pub pending_participants: Vec<ParticipantInfo>,
88
89    /// Group version (incremented on changes)
90    pub version: u64,
91
92    /// Group metadata
93    pub metadata: GroupMetadata,
94
95    /// Audit log of group operations
96    pub audit_log: Vec<GroupAuditEntry>,
97
98    /// Creation timestamp
99    pub created_at: SystemTime,
100
101    /// Last update timestamp
102    pub last_updated: SystemTime,
103}
104
105/// Participant information
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct ParticipantInfo {
108    /// Unique participant identifier
109    pub participant_id: ParticipantId,
110
111    /// ML-DSA public key for authentication (serialized ant-quic type)
112    pub public_key: Vec<u8>, // Serialized ant_quic::crypto::pqc::types::MlDsaPublicKey
113
114    /// FROST share commitment
115    pub frost_share_commitment: FrostCommitment,
116
117    /// Participant role in the group
118    pub role: ParticipantRole,
119
120    /// Status in the group
121    pub status: ParticipantStatus,
122
123    /// Join timestamp
124    pub joined_at: SystemTime,
125
126    /// Custom metadata
127    pub metadata: HashMap<String, String>,
128}
129
130/// Participant roles with hierarchical permissions
131#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
132pub enum ParticipantRole {
133    /// Can initiate all group operations
134    Leader { permissions: LeaderPermissions },
135
136    /// Can participate in threshold operations
137    Member { permissions: MemberPermissions },
138
139    /// Read-only access
140    Observer,
141}
142
143/// Leader permissions
144#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
145pub struct LeaderPermissions {
146    pub can_add_participants: bool,
147    pub can_remove_participants: bool,
148    pub can_update_threshold: bool,
149    pub can_initiate_refresh: bool,
150    pub can_assign_roles: bool,
151    pub can_create_subgroups: bool,
152}
153
154impl Default for LeaderPermissions {
155    fn default() -> Self {
156        Self {
157            can_add_participants: true,
158            can_remove_participants: true,
159            can_update_threshold: true,
160            can_initiate_refresh: true,
161            can_assign_roles: true,
162            can_create_subgroups: true,
163        }
164    }
165}
166
167/// Member permissions
168#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
169pub struct MemberPermissions {
170    pub can_sign: bool,
171    pub can_propose_operations: bool,
172    pub can_vote: bool,
173}
174
175impl Default for MemberPermissions {
176    fn default() -> Self {
177        Self {
178            can_sign: true,
179            can_propose_operations: true,
180            can_vote: true,
181        }
182    }
183}
184
185/// Participant status
186#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
187pub enum ParticipantStatus {
188    /// Active and can participate
189    Active,
190
191    /// Waiting for key ceremony completion
192    PendingJoin,
193
194    /// Marked for removal in next refresh
195    PendingRemoval,
196
197    /// Temporarily offline but still valid
198    Inactive,
199
200    /// Suspended due to misbehavior
201    Suspended { reason: String, until: SystemTime },
202}
203
204/// Group metadata
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct GroupMetadata {
207    pub name: String,
208    pub description: String,
209    pub purpose: GroupPurpose,
210    pub parent_group: Option<GroupId>,
211    pub custom_data: HashMap<String, String>,
212}
213
214/// Group purpose
215#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
216pub enum GroupPurpose {
217    /// General multi-signature
218    MultiSig,
219
220    /// Key management
221    KeyManagement,
222
223    /// Access control
224    AccessControl,
225
226    /// Governance decisions
227    Governance,
228
229    /// Custom purpose
230    Custom(String),
231}
232
233/// Group audit entry
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct GroupAuditEntry {
236    pub timestamp: SystemTime,
237    pub operation: GroupOperation,
238    pub initiator: ParticipantId,
239    pub approvers: Vec<ParticipantId>,
240    pub result: OperationResult,
241    pub metadata: HashMap<String, String>,
242}
243
244/// Group operation types
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub enum GroupOperation {
247    /// Add new participant
248    AddParticipant {
249        group_id: GroupId,
250        new_participant: ParticipantInfo,
251        new_threshold: Option<u16>,
252    },
253
254    /// Remove existing participant
255    RemoveParticipant {
256        group_id: GroupId,
257        participant_id: ParticipantId,
258        new_threshold: Option<u16>,
259    },
260
261    /// Update threshold value
262    UpdateThreshold {
263        group_id: GroupId,
264        new_threshold: u16,
265    },
266
267    /// Refresh keys (proactive security)
268    RefreshKeys { group_id: GroupId },
269
270    /// Update participant role
271    UpdateRole {
272        group_id: GroupId,
273        participant_id: ParticipantId,
274        new_role: ParticipantRole,
275    },
276
277    /// Suspend participant
278    SuspendParticipant {
279        group_id: GroupId,
280        participant_id: ParticipantId,
281        reason: String,
282        duration: std::time::Duration,
283    },
284
285    /// Create subgroup
286    CreateSubgroup {
287        parent_group_id: GroupId,
288        subgroup_config: SubgroupConfig,
289    },
290}
291
292/// Operation result
293#[derive(Debug, Clone, Serialize, Deserialize)]
294pub enum OperationResult {
295    Success,
296    Failed(String),
297    Pending,
298}
299
300/// Subgroup configuration
301#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct SubgroupConfig {
303    pub name: String,
304    pub threshold: u16,
305    pub initial_participants: Vec<ParticipantId>,
306    pub purpose: GroupPurpose,
307}
308
309/// Threshold group manager
310pub struct ThresholdGroupManager {
311    /// All managed groups
312    pub groups: HashMap<GroupId, ThresholdGroup>,
313
314    /// Local participant's shares for each group
315    pub local_shares: HashMap<GroupId, FrostKeyShare>,
316
317    /// Pending operations awaiting consensus
318    pub pending_operations: HashMap<OperationId, PendingOperation>,
319
320    /// Local participant identity
321    pub local_identity: QuantumPeerIdentity,
322}
323
324/// Operation identifier
325#[derive(Debug, Clone, PartialEq, Eq, Hash)]
326pub struct OperationId([u8; 32]);
327
328/// Pending operation awaiting consensus
329#[derive(Debug, Clone)]
330pub struct PendingOperation {
331    pub id: OperationId,
332    pub operation: GroupOperation,
333    pub proposed_at: SystemTime,
334    pub proposer: ParticipantId,
335    pub approvals: Vec<ParticipantApproval>,
336    pub rejections: Vec<ParticipantRejection>,
337    pub status: ConsensusStatus,
338}
339
340/// Participant approval
341#[derive(Debug, Clone, Serialize, Deserialize)]
342pub struct ParticipantApproval {
343    pub participant_id: ParticipantId,
344    pub signature: Vec<u8>, // Serialized ant_quic::crypto::pqc::types::MlDsaSignature
345    pub timestamp: SystemTime,
346}
347
348/// Participant rejection
349#[derive(Debug, Clone, Serialize, Deserialize)]
350pub struct ParticipantRejection {
351    pub participant_id: ParticipantId,
352    pub reason: String,
353    pub signature: Vec<u8>, // Serialized ant_quic::crypto::pqc::types::MlDsaSignature
354    pub timestamp: SystemTime,
355}
356
357/// Consensus status
358#[derive(Debug, Clone, PartialEq)]
359pub enum ConsensusStatus {
360    /// Waiting for more approvals
361    Pending,
362
363    /// Approved by threshold
364    Approved,
365
366    /// Rejected by threshold
367    Rejected,
368
369    /// Timed out
370    TimedOut,
371}
372
373impl ThresholdGroupManager {
374    /// Create a new threshold group manager
375    pub fn new(local_identity: QuantumPeerIdentity) -> Self {
376        Self {
377            groups: HashMap::new(),
378            local_shares: HashMap::new(),
379            pending_operations: HashMap::new(),
380            local_identity,
381        }
382    }
383
384    /// Create a new threshold group
385    pub async fn create_group(&mut self, config: GroupConfig) -> Result<ThresholdGroup> {
386        // Validate parameters
387        if config.threshold > config.participants.len() as u16 {
388            return Err(ThresholdError::InvalidParameters(
389                "Threshold cannot exceed number of participants".to_string(),
390            ));
391        }
392
393        if config.threshold == 0 {
394            return Err(ThresholdError::InvalidParameters(
395                "Threshold must be at least 1".to_string(),
396            ));
397        }
398
399        // Initiate DKG ceremony
400        let dkg_result = dkg::run_ceremony(config.threshold, config.participants.clone()).await?;
401
402        // Create group
403        let group = ThresholdGroup {
404            group_id: GroupId(rand::random()),
405            threshold: config.threshold,
406            participants: config.participants.len() as u16,
407            frost_group_key: dkg_result.group_key,
408            active_participants: config.participants.clone(),
409            pending_participants: Vec::new(),
410            version: 1,
411            metadata: config.metadata.clone(),
412            audit_log: vec![GroupAuditEntry {
413                timestamp: SystemTime::now(),
414                operation: GroupOperation::CreateSubgroup {
415                    parent_group_id: GroupId([0; 32]),
416                    subgroup_config: SubgroupConfig {
417                        name: config.metadata.name.clone(),
418                        threshold: config.threshold,
419                        initial_participants: config
420                            .participants
421                            .iter()
422                            .map(|p| p.participant_id.clone())
423                            .collect(),
424                        purpose: config.metadata.purpose.clone(),
425                    },
426                },
427                initiator: self.local_identity.peer_id.clone().into(),
428                approvers: vec![],
429                result: OperationResult::Success,
430                metadata: HashMap::new(),
431            }],
432            created_at: SystemTime::now(),
433            last_updated: SystemTime::now(),
434        };
435
436        // Store group and local share
437        self.groups.insert(group.group_id.clone(), group.clone());
438        self.local_shares
439            .insert(group.group_id.clone(), dkg_result.local_share);
440
441        Ok(group)
442    }
443
444    /// Propose a group operation
445    pub async fn propose_operation(&mut self, operation: GroupOperation) -> Result<OperationId> {
446        let operation_id = OperationId(rand::random());
447
448        let pending_op = PendingOperation {
449            id: operation_id.clone(),
450            operation,
451            proposed_at: SystemTime::now(),
452            proposer: self.local_identity.peer_id.clone().into(),
453            approvals: vec![],
454            rejections: vec![],
455            status: ConsensusStatus::Pending,
456        };
457
458        self.pending_operations
459            .insert(operation_id.clone(), pending_op);
460
461        // Broadcast proposal to group members
462        // Implementation would send network messages here
463
464        Ok(operation_id)
465    }
466}
467
468/// Group configuration for creation
469pub struct GroupConfig {
470    pub threshold: u16,
471    pub participants: Vec<ParticipantInfo>,
472    pub metadata: GroupMetadata,
473}
474
475/// Convert PeerId to ParticipantId (simplified for example)
476impl From<PeerId> for ParticipantId {
477    fn from(_peer_id: PeerId) -> Self {
478        // In practice, this would maintain a proper mapping
479        ParticipantId(0)
480    }
481}
482
483#[cfg(test)]
484mod tests {
485    use super::*;
486
487    #[test]
488    fn test_permission_defaults() {
489        let leader_perms = LeaderPermissions::default();
490        assert!(leader_perms.can_add_participants);
491        assert!(leader_perms.can_remove_participants);
492
493        let member_perms = MemberPermissions::default();
494        assert!(member_perms.can_sign);
495        assert!(member_perms.can_vote);
496    }
497
498    #[test]
499    fn test_participant_roles() {
500        let leader = ParticipantRole::Leader {
501            permissions: LeaderPermissions::default(),
502        };
503
504        let member = ParticipantRole::Member {
505            permissions: MemberPermissions::default(),
506        };
507
508        assert_ne!(leader, member);
509    }
510}