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)
}
}