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}