sskr/
spec.rs

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
use bc_shamir::MAX_SHARE_COUNT;

use crate::SSKRError;

/// A specification for an SSKR split.
#[derive(Debug, Clone, PartialEq)]
pub struct Spec {
    group_threshold: usize,
    groups: Vec<GroupSpec>,
}

impl Spec {
    /// Creates a new `Spec` instance with the given group threshold and groups.
    ///
    /// # Arguments
    ///
    /// * `group_threshold` - The minimum number of groups required to
    ///   reconstruct the secret.
    /// * `groups` - The list of `GroupSpec` instances that define the groups
    ///   and their members.
    ///
    /// # Errors
    ///
    /// Returns an error if the group threshold is zero, if the group threshold
    /// is greater than the number of groups, or if the number of groups is
    /// greater than the maximum share count.
    pub fn new(group_threshold: usize, groups: Vec<GroupSpec>) -> Result<Self, SSKRError> {
        if group_threshold == 0 {
            return Err(SSKRError::GroupThresholdInvalid);
        }
        if group_threshold > groups.len() {
            return Err(SSKRError::GroupThresholdInvalid);
        }
        if groups.len() > MAX_SHARE_COUNT {
            return Err(SSKRError::GroupCountInvalid);
        }
        Ok(Self {
            group_threshold,
            groups,
        })
    }

    /// Returns the group threshold.
    pub fn group_threshold(&self) -> usize {
        self.group_threshold
    }

    /// Returns a slice of the group specifications.
    pub fn groups(&self) -> &[GroupSpec] {
        &self.groups
    }

    /// Returns the number of groups.
    pub fn group_count(&self) -> usize {
        self.groups.len()
    }

    /// Returns the total number of shares across all groups.
    pub fn share_count(&self) -> usize {
        self.groups.iter().map(|g| g.member_count()).sum()
    }
}

/// A specification for a group of shares within an SSKR split.
#[derive(Debug, Clone, PartialEq)]
pub struct GroupSpec {
    member_threshold: usize,
    member_count: usize,
}

impl GroupSpec {
    /// Creates a new `GroupSpec` instance with the given member threshold and
    /// count.
    ///
    /// # Arguments
    ///
    /// * `member_threshold` - The minimum number of member shares required to
    ///   reconstruct the secret within the group.
    /// * `member_count` - The total number of member shares in the group.
    ///
    /// # Errors
    ///
    /// Returns an error if the member count is zero, if the member count is
    /// greater than the maximum share count, or if the member threshold is
    /// greater than the member count.
    pub fn new(member_threshold: usize, member_count: usize) -> Result<Self, SSKRError> {
        if member_count == 0 {
            return Err(SSKRError::MemberCountInvalid);
        }
        if member_count > MAX_SHARE_COUNT {
            return Err(SSKRError::MemberCountInvalid);
        }
        if member_threshold > member_count {
            return Err(SSKRError::MemberThresholdInvalid);
        }
        Ok(Self { member_threshold, member_count })
    }

    /// Returns the member share threshold for this group.
    pub fn member_threshold(&self) -> usize {
        self.member_threshold
    }

    /// Returns the number of member shares in this group.
    pub fn member_count(&self) -> usize {
        self.member_count
    }

    /// Parses a group specification from a string.
    pub fn parse(s: &str) -> Result<Self, SSKRError> {
        let parts: Vec<&str> = s.split('-').collect();
        if parts.len() != 3 {
            return Err(SSKRError::GroupSpecInvalid);
        }
        let member_threshold = parts[0].parse::<usize>().map_err(|_| SSKRError::GroupSpecInvalid)?;
        if parts[1] != "of" {
            return Err(SSKRError::GroupSpecInvalid);
        }
        let member_count = parts[2].parse::<usize>().map_err(|_| SSKRError::GroupSpecInvalid)?;
        Self::new(member_threshold, member_count)
    }
}

impl Default for GroupSpec {
    fn default() -> Self {
        Self::new(1, 1).unwrap()
    }
}

impl std::fmt::Display for GroupSpec {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}-of-{}", self.member_threshold, self.member_count)
    }
}