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}