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