Skip to content

Commit b1364cf

Browse files
committed
Fixed an issue where providing an incomplete group along with a complete one would throw an error.
1 parent 2fa4cb5 commit b1364cf

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed

src/encoding.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,22 +246,39 @@ fn combine_shares(shares: &[SSKRShare]) -> Result<Secret, SSKRError> {
246246
}
247247
}
248248

249+
// Check that we have enough groups to recover the master secret
249250
if next_group < group_threshold {
250251
return Err(SSKRError::NotEnoughGroups);
251252
}
252253

253-
// here, all of the shares are unpacked into member groups. Now we go through each
254+
// Here, all of the shares are unpacked into member groups. Now we go through each
254255
// group and recover the group secret, and then use the result to recover the
255256
// master secret
256257
let mut master_indexes = Vec::with_capacity(16);
257258
let mut master_shares = Vec::with_capacity(16);
258259

259260
for group in groups {
260-
let group_secret = recover_secret(&group.member_indexes, &group.member_shares)?;
261-
master_indexes.push(group.group_index);
262-
master_shares.push(group_secret);
261+
// Only attempt to recover the group secret if we have enough shares
262+
if group.member_indexes.len() < group.member_threshold {
263+
continue;
264+
}
265+
// Recover the group secret
266+
if let Ok(group_secret) = recover_secret(&group.member_indexes, &group.member_shares) {
267+
master_indexes.push(group.group_index);
268+
master_shares.push(group_secret);
269+
}
270+
// Stop if we have enough groups to recover the master secret
271+
if master_indexes.len() == group_threshold {
272+
break;
273+
}
274+
}
275+
276+
// If we don't have enough groups to recover the master secret, return an error
277+
if master_indexes.len() < group_threshold {
278+
return Err(SSKRError::NotEnoughGroups);
263279
}
264280

281+
// Recover the master secret
265282
let master_secret = recover_secret(&master_indexes, &master_shares)?;
266283
let master_secret = Secret::new(master_secret)?;
267284

src/lib.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,4 +424,27 @@ mod tests {
424424
assert_eq!(from_utf8(result.unwrap().data()).unwrap(), TEXT);
425425
}
426426
}
427+
428+
/// Test fix for [seedtool-cli #6](https://github.com/BlockchainCommons/seedtool-cli-rust/issues/6).
429+
#[test]
430+
fn example_encode_4() {
431+
use crate::{ Secret, GroupSpec, Spec, sskr_generate, sskr_combine };
432+
use std::str::from_utf8;
433+
434+
const TEXT: &str = "my secret belongs to me.";
435+
let secret = Secret::new(TEXT).unwrap();
436+
let spec = Spec::new(1, vec![GroupSpec::new(2, 3).unwrap(), GroupSpec::new(2, 3).unwrap()]).unwrap();
437+
let groupd_shares: Vec<Vec<Vec<u8>>> = sskr_generate(&spec, &secret).unwrap();
438+
let flattened_shares = groupd_shares.into_iter().flatten().collect::<Vec<Vec<u8>>>();
439+
// The group threshold is 1, but we're providing an additional share from the second group.
440+
// This was previously causing an error, because the second group could not be decoded.
441+
// The correct behavior is to ignore any group's shares that cannot be decoded.
442+
let recovered_share_indexes = [0, 1, 3];
443+
let recovered_shares = recovered_share_indexes
444+
.iter()
445+
.map(|index| flattened_shares[*index].clone())
446+
.collect::<Vec<Vec<u8>>>();
447+
let recovered_secret = sskr_combine(&recovered_shares).unwrap();
448+
assert_eq!(from_utf8(recovered_secret.data()).unwrap(), TEXT);
449+
}
427450
}

0 commit comments

Comments
 (0)