sskr/
spec.rs

1use bc_shamir::MAX_SHARE_COUNT;
2
3use crate::{Error, Result};
4
5/// A specification for an SSKR split.
6#[derive(Debug, Clone, PartialEq)]
7pub struct Spec {
8    group_threshold: usize,
9    groups: Vec<GroupSpec>,
10}
11
12impl Spec {
13    /// Creates a new `Spec` instance with the given group threshold and groups.
14    ///
15    /// # Arguments
16    ///
17    /// * `group_threshold` - The minimum number of groups required to
18    ///   reconstruct the secret.
19    /// * `groups` - The list of `GroupSpec` instances that define the groups
20    ///   and their members.
21    ///
22    /// # Errors
23    ///
24    /// Returns an error if the group threshold is zero, if the group threshold
25    /// is greater than the number of groups, or if the number of groups is
26    /// greater than the maximum share count.
27    pub fn new(group_threshold: usize, groups: Vec<GroupSpec>) -> Result<Self> {
28        if group_threshold == 0 {
29            return Err(Error::GroupThresholdInvalid);
30        }
31        if group_threshold > groups.len() {
32            return Err(Error::GroupThresholdInvalid);
33        }
34        if groups.len() > MAX_SHARE_COUNT {
35            return Err(Error::GroupCountInvalid);
36        }
37        Ok(Self { group_threshold, groups })
38    }
39
40    /// Returns the group threshold.
41    pub fn group_threshold(&self) -> usize { self.group_threshold }
42
43    /// Returns a slice of the group specifications.
44    pub fn groups(&self) -> &[GroupSpec] { &self.groups }
45
46    /// Returns the number of groups.
47    pub fn group_count(&self) -> usize { self.groups.len() }
48
49    /// Returns the total number of shares across all groups.
50    pub fn share_count(&self) -> usize {
51        self.groups.iter().map(|g| g.member_count()).sum()
52    }
53}
54
55/// A specification for a group of shares within an SSKR split.
56#[derive(Debug, Clone, PartialEq)]
57pub struct GroupSpec {
58    member_threshold: usize,
59    member_count: usize,
60}
61
62impl GroupSpec {
63    /// Creates a new `GroupSpec` instance with the given member threshold and
64    /// count.
65    ///
66    /// # Arguments
67    ///
68    /// * `member_threshold` - The minimum number of member shares required to
69    ///   reconstruct the secret within the group.
70    /// * `member_count` - The total number of member shares in the group.
71    ///
72    /// # Errors
73    ///
74    /// Returns an error if the member count is zero, if the member count is
75    /// greater than the maximum share count, or if the member threshold is
76    /// greater than the member count.
77    pub fn new(member_threshold: usize, member_count: usize) -> Result<Self> {
78        if member_count == 0 {
79            return Err(Error::MemberCountInvalid);
80        }
81        if member_count > MAX_SHARE_COUNT {
82            return Err(Error::MemberCountInvalid);
83        }
84        if member_threshold > member_count {
85            return Err(Error::MemberThresholdInvalid);
86        }
87        Ok(Self { member_threshold, member_count })
88    }
89
90    /// Returns the member share threshold for this group.
91    pub fn member_threshold(&self) -> usize { self.member_threshold }
92
93    /// Returns the number of member shares in this group.
94    pub fn member_count(&self) -> usize { self.member_count }
95
96    /// Parses a group specification from a string.
97    pub fn parse(s: &str) -> Result<Self> {
98        let parts: Vec<&str> = s.split('-').collect();
99        if parts.len() != 3 {
100            return Err(Error::GroupSpecInvalid);
101        }
102        let member_threshold = parts[0]
103            .parse::<usize>()
104            .map_err(|_| Error::GroupSpecInvalid)?;
105        if parts[1] != "of" {
106            return Err(Error::GroupSpecInvalid);
107        }
108        let member_count = parts[2]
109            .parse::<usize>()
110            .map_err(|_| Error::GroupSpecInvalid)?;
111        Self::new(member_threshold, member_count)
112    }
113}
114
115impl Default for GroupSpec {
116    fn default() -> Self { Self::new(1, 1).unwrap() }
117}
118
119impl std::fmt::Display for GroupSpec {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        write!(f, "{}-of-{}", self.member_threshold, self.member_count)
122    }
123}