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