bc_components/
sskr_mod.rs

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