|
1 | 1 | pub const MIN_SECRET_LEN: usize = bc_shamir::MIN_SECRET_LEN;
|
2 | 2 | pub const MAX_SECRET_LEN: usize = bc_shamir::MAX_SECRET_LEN;
|
3 | 3 | pub const MAX_SHARE_COUNT: usize = bc_shamir::MAX_SHARE_COUNT;
|
| 4 | +pub const MAX_GROUPS_COUNT: usize = MAX_SHARE_COUNT; |
4 | 5 | pub const METADATA_LENGTH_BYTES: usize = 5;
|
5 | 6 | pub const MIN_SERIALIZE_LENGTH_BYTES: usize = METADATA_LENGTH_BYTES + MIN_SECRET_LEN;
|
6 | 7 |
|
@@ -102,20 +103,137 @@ mod tests {
|
102 | 103 | let group2 = GroupSpec::new(2, 3).unwrap();
|
103 | 104 | let spec = Spec::new(2, vec![group1, group2]).unwrap();
|
104 | 105 | let shares = sskr_generate_using(&spec, &secret, &mut rng).unwrap();
|
105 |
| - println!("shares: {:?}", shares); |
| 106 | + // println!("shares: {:?}", shares); |
106 | 107 | assert_eq!(shares.len(), 2);
|
107 | 108 | assert_eq!(shares[0].len(), 3);
|
108 | 109 | assert_eq!(shares[1].len(), 3);
|
109 | 110 | let flattened_shares = shares.into_iter().flatten().collect::<Vec<_>>();
|
110 | 111 | assert_eq!(flattened_shares.len(), 6);
|
111 | 112 | for share in &flattened_shares {
|
112 | 113 | assert_eq!(share.len(), METADATA_LENGTH_BYTES + secret.len());
|
113 |
| - println!("share: {}", hex::encode(share)); |
| 114 | + // println!("share: {}", hex::encode(share)); |
114 | 115 | }
|
115 | 116 |
|
116 | 117 | let recovered_share_indexes = vec![0, 1, 3, 5];
|
117 | 118 | let recovered_shares = recovered_share_indexes.iter().map(|index| flattened_shares[*index].clone()).collect::<Vec<_>>();
|
118 | 119 | let recovered_secret = sskr_combine(&recovered_shares).unwrap();
|
119 | 120 | assert_eq!(recovered_secret, secret);
|
120 | 121 | }
|
| 122 | + |
| 123 | + fn fisher_yates_shuffle<T>(slice: &mut [T], rng: &mut impl RandomNumberGenerator) { |
| 124 | + let mut i = slice.len(); |
| 125 | + while i > 1 { |
| 126 | + i -= 1; |
| 127 | + let j = rng.next_in_closed_range(&(0..=i)); |
| 128 | + slice.swap(i, j); |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + #[test] |
| 133 | + fn test_shuffle() { |
| 134 | + let mut rng = bc_crypto::make_fake_random_number_generator(); |
| 135 | + let mut v = (0..100).collect::<Vec<_>>(); |
| 136 | + fisher_yates_shuffle(&mut v, &mut rng); |
| 137 | + assert_eq!(v.len(), 100); |
| 138 | + assert_eq!(v, [79, 70, 40, 53, 25, 30, 31, 88, 10, 1, |
| 139 | + 45, 54, 81, 58, 55, 59, 69, 78, 65, 47, |
| 140 | + 75, 61, 0, 72, 20, 9, 80, 13, 73, 11, |
| 141 | + 60, 56, 19, 42, 33, 12, 36, 38, 6, 35, |
| 142 | + 68, 77, 50, 18, 97, 49, 98, 85, 89, 91, |
| 143 | + 15, 71, 99, 67, 84, 23, 64, 14, 57, 48, |
| 144 | + 62, 29, 28, 94, 44, 8, 66, 34, 43, 21, |
| 145 | + 63, 16, 92, 95, 27, 51, 26, 86, 22, 41, |
| 146 | + 93, 82, 7, 87, 74, 37, 46, 3, 96, 24, |
| 147 | + 90, 39, 32, 17, 76, 4, 83, 2, 52, 5]); |
| 148 | + } |
| 149 | + |
| 150 | + struct RecoverSpec { |
| 151 | + secret: Secret, |
| 152 | + spec: Spec, |
| 153 | + shares: Vec<Vec<Vec<u8>>>, |
| 154 | + recovered_group_indexes: Vec<usize>, |
| 155 | + recovered_member_indexes: Vec<Vec<usize>>, |
| 156 | + recovered_shares: Vec<Vec<u8>>, |
| 157 | + } |
| 158 | + |
| 159 | + impl RecoverSpec { |
| 160 | + fn new(secret: Secret, spec: Spec, shares: Vec<Vec<Vec<u8>>>, rng: &mut impl RandomNumberGenerator) -> Self { |
| 161 | + let mut group_indexes = (0..spec.group_count()).collect::<Vec<_>>(); |
| 162 | + fisher_yates_shuffle(&mut group_indexes, rng); |
| 163 | + let recovered_group_indexes = group_indexes[..spec.group_threshold()].to_vec(); |
| 164 | + let mut recovered_member_indexes = Vec::new(); |
| 165 | + for group_index in &recovered_group_indexes { |
| 166 | + let group = &spec.groups()[*group_index]; |
| 167 | + let mut member_indexes = (0..group.member_count()).collect::<Vec<_>>(); |
| 168 | + fisher_yates_shuffle(&mut member_indexes, rng); |
| 169 | + let recovered_member_indexes_for_group = member_indexes[..group.member_threshold()].to_vec(); |
| 170 | + recovered_member_indexes.push(recovered_member_indexes_for_group); |
| 171 | + } |
| 172 | + |
| 173 | + let mut recovered_shares = Vec::new(); |
| 174 | + for (i, recovered_group_index) in recovered_group_indexes.iter().enumerate() { |
| 175 | + let group_shares = &shares[*recovered_group_index]; |
| 176 | + for recovered_member_index in &recovered_member_indexes[i] { |
| 177 | + let member_share = &group_shares[*recovered_member_index]; |
| 178 | + recovered_shares.push(member_share.clone()); |
| 179 | + } |
| 180 | + } |
| 181 | + fisher_yates_shuffle(&mut recovered_shares, rng); |
| 182 | + |
| 183 | + Self { |
| 184 | + secret, |
| 185 | + spec, |
| 186 | + shares, |
| 187 | + recovered_group_indexes, |
| 188 | + recovered_member_indexes, |
| 189 | + recovered_shares, |
| 190 | + } |
| 191 | + } |
| 192 | + |
| 193 | + fn recover(&self) { |
| 194 | + let success = match sskr_combine(&self.recovered_shares) { |
| 195 | + Ok(recovered_secret) => recovered_secret == self.secret, |
| 196 | + Err(e) => { |
| 197 | + println!("error: {:?}", e); |
| 198 | + false |
| 199 | + }, |
| 200 | + }; |
| 201 | + |
| 202 | + if !success { |
| 203 | + println!("secret: {}", hex::encode(self.secret.data())); |
| 204 | + println!("spec: {:?}", self.spec); |
| 205 | + println!("shares: {:?}", self.shares); |
| 206 | + println!("recovered_group_indexes: {:?}", self.recovered_group_indexes); |
| 207 | + println!("recovered_member_indexes: {:?}", self.recovered_member_indexes); |
| 208 | + println!("recovered_shares: {:?}", &self.recovered_shares); |
| 209 | + panic!(); |
| 210 | + } |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + fn one_fuzz_test(rng: &mut impl RandomNumberGenerator) { |
| 215 | + let secret_len = rng.next_in_closed_range(&(MIN_SECRET_LEN..=MAX_SECRET_LEN)) & !1; |
| 216 | + let secret = Secret::new(rng.random_data(secret_len)).unwrap(); |
| 217 | + let group_count = rng.next_in_closed_range(&(1..=MAX_GROUPS_COUNT)); |
| 218 | + let group_specs = (0..group_count).map(|_| { |
| 219 | + let member_count = rng.next_in_closed_range(&(1..=MAX_SHARE_COUNT)); |
| 220 | + let member_threshold = rng.next_in_closed_range(&(1..=member_count)); |
| 221 | + GroupSpec::new(member_threshold, member_count).unwrap() |
| 222 | + }).collect::<Vec<_>>(); |
| 223 | + let group_threshold = rng.next_in_closed_range(&(1..=group_count)); |
| 224 | + let spec = Spec::new(group_threshold, group_specs).unwrap(); |
| 225 | + let shares = sskr_generate_using(&spec, &secret, rng).unwrap(); |
| 226 | + |
| 227 | + let recover_spec = RecoverSpec::new(secret, spec, shares, rng); |
| 228 | + recover_spec.recover(); |
| 229 | + } |
| 230 | + |
| 231 | + #[test] |
| 232 | + fn fuzz_test() { |
| 233 | + let mut rng = bc_crypto::make_fake_random_number_generator(); |
| 234 | + // let mut rng = bc_crypto::SecureRandomNumberGenerator; |
| 235 | + for _ in 0..100 { |
| 236 | + one_fuzz_test(&mut rng); |
| 237 | + } |
| 238 | + } |
121 | 239 | }
|
0 commit comments