bc_components/
sskr_mod.rs

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