bc_components/
sskr_mod.rs

1use bc_rand::{ RandomNumberGenerator, SecureRandomNumberGenerator };
2use bc_ur::prelude::*;
3use sskr::SSKRError;
4use anyhow::{ Result, Error };
5use crate::tags;
6
7/// Re-export of the `Spec` type from the `sskr` crate.
8///
9/// Describes the configuration for a Sharded Secret Key Reconstruction (SSKR) split,
10/// including the group threshold and specifications for each group.
11pub use sskr::{ Spec as SSKRSpec, GroupSpec as SSKRGroupSpec, Secret as SSKRSecret };
12
13/// A share of a secret split using Sharded Secret Key Reconstruction (SSKR).
14///
15/// SSKR is a protocol for splitting a secret into multiple shares across one or more
16/// groups, such that the secret can be reconstructed only when a threshold number of
17/// shares from a threshold number of groups are combined.
18///
19/// Each SSKR share contains:
20/// - A unique identifier for the split
21/// - Metadata about the group structure (thresholds, counts, indices)
22/// - A portion of the secret data
23///
24/// SSKR shares follow a specific binary format that includes a 5-byte metadata header
25/// followed by the share value. The metadata encodes information about group thresholds,
26/// member thresholds, and the position of this share within the overall structure.
27#[derive(Debug, PartialEq, Eq, Clone, Hash)]
28pub struct SSKRShare(Vec<u8>);
29
30impl SSKRShare {
31    /// Creates a new `SSKRShare` from raw binary data.
32    ///
33    /// # Parameters
34    ///
35    /// * `data` - The raw binary data of the SSKR share, including both metadata (5 bytes)
36    ///   and share value.
37    ///
38    /// # Returns
39    ///
40    /// A new `SSKRShare` instance containing the provided data.
41    ///
42    /// # Example
43    ///
44    /// ```
45    /// use bc_components::SSKRShare;
46    ///
47    /// // Raw SSKR share data (typically from sskr_generate function)
48    /// let data = vec![0x12, 0x34, 0x21, 0x31, 0x01, 0xAA, 0xBB, 0xCC]; // Example data
49    /// let share = SSKRShare::from_data(data);
50    /// ```
51    pub fn from_data(data: impl Into<Vec<u8>>) -> Self {
52        Self(data.into())
53    }
54
55    /// Returns a reference to the raw binary data of this share.
56    ///
57    /// # Returns
58    ///
59    /// A reference to the byte vector containing the SSKR share data.
60    ///
61    /// # Example
62    ///
63    /// ```
64    /// use bc_components::SSKRShare;
65    ///
66    /// let data = vec![0x12, 0x34, 0x21, 0x31, 0x01, 0xAA, 0xBB, 0xCC]; // Example data
67    /// let share = SSKRShare::from_data(data.clone());
68    /// assert_eq!(share.data(), &data);
69    /// ```
70    pub fn data(&self) -> &Vec<u8> {
71        &self.0
72    }
73
74    /// Creates a new `SSKRShare` from a hexadecimal string.
75    ///
76    /// # Parameters
77    ///
78    /// * `hex` - A hexadecimal string representing the SSKR share data.
79    ///
80    /// # Returns
81    ///
82    /// A new `SSKRShare` instance created from the decoded hex data.
83    ///
84    /// # Panics
85    ///
86    /// Panics if the hex string is invalid and cannot be decoded.
87    ///
88    /// # Example
89    ///
90    /// ```
91    /// use bc_components::SSKRShare;
92    ///
93    /// let share = SSKRShare::from_hex("1234213101aabbcc");
94    /// assert_eq!(share.hex(), "1234213101aabbcc");
95    /// ```
96    pub fn from_hex(hex: impl AsRef<str>) -> Self {
97        Self::from_data(hex::decode(hex.as_ref()).unwrap())
98    }
99
100    /// Returns the data of this `SSKRShare` as a hexadecimal string.
101    ///
102    /// # Returns
103    ///
104    /// A hex-encoded string representing the SSKR share data.
105    ///
106    /// # Example
107    ///
108    /// ```
109    /// use bc_components::SSKRShare;
110    ///
111    /// let share = SSKRShare::from_data(vec![0x12, 0x34, 0x21, 0x31, 0x01, 0xAA, 0xBB, 0xCC]);
112    /// assert_eq!(share.hex(), "1234213101aabbcc");
113    /// ```
114    pub fn hex(&self) -> String {
115        hex::encode(self.data())
116    }
117
118    /// Returns the unique identifier of the split to which this share belongs.
119    ///
120    /// The identifier is a 16-bit value that is the same for all shares in a split
121    /// and is used to verify that shares belong together when combining them.
122    ///
123    /// # Returns
124    ///
125    /// A 16-bit integer representing the unique identifier of the split.
126    ///
127    /// # Example
128    ///
129    /// ```
130    /// use bc_components::SSKRShare;
131    ///
132    /// let share = SSKRShare::from_data(vec![0x12, 0x34, 0x21, 0x31, 0x01, 0xAA, 0xBB, 0xCC]);
133    /// assert_eq!(share.identifier(), 0x1234);
134    /// ```
135    pub fn identifier(&self) -> u16 {
136        (u16::from(self.0[0]) << 8) | u16::from(self.0[1])
137    }
138
139    /// Returns the unique identifier of the split as a hexadecimal string.
140    ///
141    /// # Returns
142    ///
143    /// A hexadecimal string representing the 16-bit identifier.
144    ///
145    /// # Example
146    ///
147    /// ```
148    /// use bc_components::SSKRShare;
149    ///
150    /// let share = SSKRShare::from_data(vec![0x12, 0x34, 0x21, 0x31, 0x01, 0xAA, 0xBB, 0xCC]);
151    /// assert_eq!(share.identifier_hex(), "1234");
152    /// ```
153    pub fn identifier_hex(&self) -> String {
154        hex::encode(&self.0[0..=1])
155    }
156
157    /// Returns the minimum number of groups whose quorum must be met to
158    /// reconstruct the secret.
159    ///
160    /// This value is encoded as GroupThreshold - 1 in the metadata, so the actual
161    /// threshold value is one more than the encoded value.
162    ///
163    /// # Returns
164    ///
165    /// The group threshold value (minimum number of groups required).
166    ///
167    /// # Example
168    ///
169    /// ```
170    /// use bc_components::SSKRShare;
171    ///
172    /// let share = SSKRShare::from_data(vec![0x12, 0x34, 0x21, 0x31, 0x01, 0xAA, 0xBB, 0xCC]);
173    /// // The encoded value 0x2 in the third byte's high nibble represents a threshold of 3
174    /// assert_eq!(share.group_threshold(), 3);
175    /// ```
176    pub fn group_threshold(&self) -> usize {
177        usize::from(self.0[2] >> 4) + 1
178    }
179
180    /// Returns the total number of groups in the split.
181    ///
182    /// This value is encoded as GroupCount - 1 in the metadata, so the actual
183    /// count is one more than the encoded value.
184    ///
185    /// # Returns
186    ///
187    /// The total number of groups in the split.
188    ///
189    /// # Example
190    ///
191    /// ```
192    /// use bc_components::SSKRShare;
193    ///
194    /// let share = SSKRShare::from_data(vec![0x12, 0x34, 0x21, 0x31, 0x01, 0xAA, 0xBB, 0xCC]);
195    /// // The encoded value 0x1 in the third byte's low nibble represents a count of 2
196    /// assert_eq!(share.group_count(), 2);
197    /// ```
198    pub fn group_count(&self) -> usize {
199        usize::from(self.0[2] & 0xf) + 1
200    }
201
202    /// Returns the index of the group to which this share belongs.
203    ///
204    /// This is a zero-based index identifying which group in the split this
205    /// share is part of.
206    ///
207    /// # Returns
208    ///
209    /// The group index (0-based).
210    ///
211    /// # Example
212    ///
213    /// ```
214    /// use bc_components::SSKRShare;
215    ///
216    /// let share = SSKRShare::from_data(vec![0x12, 0x34, 0x21, 0x31, 0x01, 0xAA, 0xBB, 0xCC]);
217    /// // The encoded value 0x3 in the fourth byte's high nibble represents group index 3
218    /// assert_eq!(share.group_index(), 3);
219    /// ```
220    pub fn group_index(&self) -> usize {
221        usize::from(self.0[3] >> 4)
222    }
223
224    /// Returns the minimum number of shares within the group to which this share
225    /// belongs that must be combined to meet the group threshold.
226    ///
227    /// This value is encoded as MemberThreshold - 1 in the metadata, so the actual
228    /// threshold value is one more than the encoded value.
229    ///
230    /// # Returns
231    ///
232    /// The member threshold value (minimum number of shares required within this group).
233    ///
234    /// # Example
235    ///
236    /// ```
237    /// use bc_components::SSKRShare;
238    ///
239    /// let share = SSKRShare::from_data(vec![0x12, 0x34, 0x21, 0x31, 0x01, 0xAA, 0xBB, 0xCC]);
240    /// // The encoded value 0x1 in the fourth byte's low nibble represents a threshold of 2
241    /// assert_eq!(share.member_threshold(), 2);
242    /// ```
243    pub fn member_threshold(&self) -> usize {
244        usize::from(self.0[3] & 0xf) + 1
245    }
246
247    /// Returns the index of this share within the group to which it belongs.
248    ///
249    /// This is a zero-based index identifying which share within the group
250    /// this specific share is.
251    ///
252    /// # Returns
253    ///
254    /// The member index (0-based) within the group.
255    ///
256    /// # Example
257    ///
258    /// ```
259    /// use bc_components::SSKRShare;
260    ///
261    /// let share = SSKRShare::from_data(vec![0x12, 0x34, 0x21, 0x31, 0x01, 0xAA, 0xBB, 0xCC]);
262    /// // The encoded value 0x1 in the fifth byte's low nibble represents member index 1
263    /// assert_eq!(share.member_index(), 1);
264    /// ```
265    pub fn member_index(&self) -> usize {
266        usize::from(self.0[4] & 0xf)
267    }
268}
269
270/// Implementation of the CBOR Tagged trait for SSKRShare.
271///
272/// This allows SSKR shares to be serialized with specific CBOR tags that
273/// identify them as SSKR shares.
274impl CBORTagged for SSKRShare {
275    fn cbor_tags() -> Vec<Tag> {
276        tags_for_values(&[tags::TAG_SSKR_SHARE, tags::TAG_SSKR_SHARE_V1])
277    }
278}
279
280/// Conversion from SSKRShare to CBOR for serialization.
281impl From<SSKRShare> for CBOR {
282    fn from(value: SSKRShare) -> Self {
283        value.tagged_cbor()
284    }
285}
286
287/// Implementation of CBOR encoding for SSKRShare.
288impl CBORTaggedEncodable for SSKRShare {
289    fn untagged_cbor(&self) -> CBOR {
290        CBOR::to_byte_string(&self.0)
291    }
292}
293
294/// Conversion from CBOR to SSKRShare for deserialization.
295impl TryFrom<CBOR> for SSKRShare {
296    type Error = Error;
297
298    fn try_from(cbor: CBOR) -> Result<Self, Self::Error> {
299        Self::from_tagged_cbor(cbor)
300    }
301}
302
303/// Implementation of CBOR decoding for SSKRShare.
304impl CBORTaggedDecodable for SSKRShare {
305    fn from_untagged_cbor(cbor: CBOR) -> Result<Self> {
306        let data = CBOR::try_into_byte_string(cbor)?;
307        let instance = Self::from_data(data);
308        Ok(instance)
309    }
310}
311
312/// Generates SSKR shares for the given `Spec` and `Secret`.
313///
314/// This function splits a master secret into multiple shares according to the
315/// specified group and member thresholds, using a secure random number generator.
316///
317/// # Parameters
318///
319/// * `spec` - The `SSKRSpec` instance that defines the group threshold, number of groups,
320///   and the member thresholds for each group.
321/// * `master_secret` - The `SSKRSecret` instance to be split into shares.
322///
323/// # Returns
324///
325/// A result containing a nested vector of `SSKRShare` instances if successful,
326/// or an `SSKRError` if the operation fails. The outer vector contains one vector
327/// per group, and each inner vector contains the shares for that group.
328///
329/// # Errors
330///
331/// Returns an error if:
332/// - The secret is too short or too long
333/// - The group threshold is invalid
334/// - The member thresholds are invalid
335/// - Any other error in the underlying SSKR implementation
336///
337/// # Example
338///
339/// ```
340/// use bc_components::{SSKRSecret, SSKRSpec, SSKRGroupSpec, sskr_generate};
341///
342/// // Create a master secret from a byte array (must be exactly 16 or 32 bytes)
343/// let master_secret = SSKRSecret::new(b"0123456789abcdef").unwrap(); // Exactly 16 bytes
344///
345/// // Configure a split with 2 groups, requiring both groups (threshold = 2)
346/// // First group: 2 of 3 shares needed
347/// // Second group: 3 of 5 shares needed
348/// let group1 = SSKRGroupSpec::new(2, 3).unwrap();
349/// let group2 = SSKRGroupSpec::new(3, 5).unwrap();
350/// let spec = SSKRSpec::new(2, vec![group1, group2]).unwrap();
351///
352/// // Generate the shares
353/// let shares = sskr_generate(&spec, &master_secret).unwrap();
354///
355/// // Verify the structure matches our specification
356/// assert_eq!(shares.len(), 2);           // 2 groups
357/// assert_eq!(shares[0].len(), 3);        // 3 shares in first group
358/// assert_eq!(shares[1].len(), 5);        // 5 shares in second group
359/// ```
360pub fn sskr_generate(
361    spec: &SSKRSpec,
362    master_secret: &SSKRSecret
363) -> Result<Vec<Vec<SSKRShare>>, SSKRError> {
364    let mut rng = SecureRandomNumberGenerator;
365    sskr_generate_using(spec, master_secret, &mut rng)
366}
367
368/// Generates SSKR shares using a custom random number generator.
369///
370/// This function is similar to `sskr_generate`, but allows specifying a custom
371/// random number generator for deterministic testing or other specialized needs.
372///
373/// # Parameters
374///
375/// * `spec` - The `SSKRSpec` instance that defines the group threshold, number of groups,
376///   and the member thresholds for each group.
377/// * `master_secret` - The `SSKRSecret` instance to be split into shares.
378/// * `rng` - The random number generator to use for generating shares.
379///
380/// # Returns
381///
382/// A result containing a nested vector of `SSKRShare` instances if successful,
383/// or an `SSKRError` if the operation fails. The outer vector contains one vector
384/// per group, and each inner vector contains the shares for that group.
385///
386/// # Errors
387///
388/// Returns an error if:
389/// - The secret is too short or too long
390/// - The group threshold is invalid
391/// - The member thresholds are invalid
392/// - Any other error in the underlying SSKR implementation
393///
394/// # Example
395///
396/// ```
397/// use bc_components::{SSKRSecret, SSKRSpec, SSKRGroupSpec, sskr_generate_using};
398/// use bc_rand::SecureRandomNumberGenerator;
399///
400/// // Create a master secret from a byte array (must be exactly 16 or 32 bytes)
401/// let master_secret = SSKRSecret::new(b"0123456789abcdef").unwrap(); // Exactly 16 bytes
402///
403/// // Configure a split with 2 groups, requiring both groups (threshold = 2)
404/// let group1 = SSKRGroupSpec::new(2, 3).unwrap();
405/// let group2 = SSKRGroupSpec::new(3, 5).unwrap();
406/// let spec = SSKRSpec::new(2, vec![group1, group2]).unwrap();
407///
408/// // Generate the shares with a custom RNG
409/// let mut rng = SecureRandomNumberGenerator;
410/// let shares = sskr_generate_using(&spec, &master_secret, &mut rng).unwrap();
411///
412/// // Verify the structure
413/// assert_eq!(shares.len(), 2);
414/// assert_eq!(shares[0].len(), 3);
415/// assert_eq!(shares[1].len(), 5);
416/// ```
417pub fn sskr_generate_using(
418    spec: &SSKRSpec,
419    master_secret: &SSKRSecret,
420    rng: &mut impl RandomNumberGenerator
421) -> Result<Vec<Vec<SSKRShare>>, SSKRError> {
422    let shares = sskr::sskr_generate_using(spec, master_secret, rng)?;
423    let shares = shares
424        .into_iter()
425        .map(|group| {
426            group
427                .into_iter()
428                .map(|share| { SSKRShare::from_data(share) })
429                .collect()
430        })
431        .collect();
432    Ok(shares)
433}
434
435/// Combines SSKR shares to reconstruct the original secret.
436///
437/// This function takes a collection of shares and attempts to reconstruct the
438/// original secret. The shares must meet the group and member thresholds specified
439/// when the shares were generated.
440///
441/// # Parameters
442///
443/// * `shares` - A slice of `SSKRShare` instances to be combined.
444///
445/// # Returns
446///
447/// A result containing the reconstructed `SSKRSecret` if successful,
448/// or an `SSKRError` if the shares cannot be combined.
449///
450/// # Errors
451///
452/// Returns an error if:
453/// - The shares don't all belong to the same split (different identifiers)
454/// - There are insufficient shares to meet the group threshold
455/// - There are insufficient shares within each group to meet their member thresholds
456/// - The shares are malformed or corrupted
457///
458/// # Example
459///
460/// ```
461/// use bc_components::{SSKRSecret, SSKRSpec, SSKRGroupSpec, SSKRShare, sskr_generate, sskr_combine};
462///
463/// // Create a master secret (must be exactly 16 or 32 bytes)
464/// let master_secret = SSKRSecret::new(b"0123456789abcdef").unwrap(); // Exactly 16 bytes
465///
466/// // Configure a split with 2 groups, requiring both groups (threshold = 2)
467/// let group1 = SSKRGroupSpec::new(2, 3).unwrap();
468/// let group2 = SSKRGroupSpec::new(3, 5).unwrap();
469/// let spec = SSKRSpec::new(2, vec![group1, group2]).unwrap();
470///
471/// // Generate the shares
472/// let shares = sskr_generate(&spec, &master_secret).unwrap();
473///
474/// // Collect shares that meet the threshold requirements
475/// let recovery_shares = vec![
476///     // Two shares from group 1 (meets threshold of 2)
477///     shares[0][0].clone(),
478///     shares[0][1].clone(),
479///     
480///     // Three shares from group 2 (meets threshold of 3)
481///     shares[1][0].clone(),
482///     shares[1][1].clone(),
483///     shares[1][2].clone(),
484/// ];
485///
486/// // Recover the original secret
487/// let recovered_secret = sskr_combine(&recovery_shares).unwrap();
488/// assert_eq!(recovered_secret, master_secret);
489/// ```
490pub fn sskr_combine(shares: &[SSKRShare]) -> Result<SSKRSecret, SSKRError> {
491    let shares: Vec<Vec<u8>> = shares
492        .iter()
493        .map(|share| share.data().to_vec())
494        .collect();
495    sskr::sskr_combine(&shares)
496}