Skip to content

Commit 861b6b2

Browse files
authored
[❄] Improve check_fingerprint return value (#241)
In practice you want to distinguish between the fingerprint matching because of the pathological case (threshold=1) and when it actually matches.
1 parent db463d8 commit 861b6b2

File tree

2 files changed

+29
-16
lines changed

2 files changed

+29
-16
lines changed

schnorr_fun/src/frost/chilldkg/encpedpop.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,11 @@ impl AggKeygenInput {
231231
let tweak_poly = shared_key.grind_fingerprint::<H>(fingerprint);
232232
// replace our poly with the one that has the fingerprint
233233
self.inner.agg_poly = shared_key.point_polynomial()[1..].to_vec();
234-
debug_assert!(self.shared_key().check_fingerprint::<H>(fingerprint));
234+
debug_assert!(
235+
self.shared_key()
236+
.check_fingerprint::<H>(fingerprint)
237+
.is_some()
238+
);
235239

236240
for (share_index, (_encryption_key, encrypted_secret_share)) in &mut self.encrypted_shares {
237241
// 💡 The share encryption is homomorphic so we can apply the tweak
@@ -556,8 +560,10 @@ mod test {
556560
for share in paired_shares {
557561
assert_eq!(shared_key.pair_secret_share(*share.secret_share()), Some(share));
558562
}
563+
let bits_matched = shared_key.check_fingerprint::<sha2::Sha256>(fingerprint).unwrap();
559564

560-
assert!(shared_key.check_fingerprint::<sha2::Sha256>(fingerprint), "fingerprint was grinded correctly");
565+
let should_have_matched = ((threshold - 1) * bits_per_coeff as u32).min(max_bits_total as u32);
566+
assert_eq!(bits_matched, should_have_matched as usize, "fingerprint was grinded correctly");
561567
}
562568
}
563569

schnorr_fun/src/frost/shared_key.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -193,15 +193,21 @@ impl<T: Normalized, Z: ZeroChoice> SharedKey<T, Z> {
193193
/// This allows detection of shares from the same DKG session and helps
194194
/// identify corrupted or mismatched shares.
195195
///
196-
/// Returns `true` if all coefficients match the fingerprint pattern, `false`
197-
/// if any coefficient fails to meet the difficulty requirement.
198-
pub fn check_fingerprint<H: crate::fun::hash::Hash32>(&self, fingerprint: Fingerprint) -> bool {
196+
/// Returns `Some(n)` where `n` is the number of bits verified if
197+
/// successful, or `None` if verification failed. Returns `Some(0)` if the
198+
/// polynomial is too short (≤1 coefficients) to contain a fingerprint. It
199+
/// will never return more than `max_bits_total` which indicates a complete
200+
/// match.
201+
pub fn check_fingerprint<H: crate::fun::hash::Hash32>(
202+
&self,
203+
fingerprint: Fingerprint,
204+
) -> Option<usize> {
199205
use crate::fun::hash::HashAdd;
200206

201207
// the fingerprint is only placed on the non-constant coefficients so it
202208
// can't be detected with a length 1 polynomial
203209
if self.point_polynomial.len() <= 1 {
204-
return true;
210+
return Some(0);
205211
}
206212

207213
let mut hash_state = H::default()
@@ -214,20 +220,19 @@ impl<T: Normalized, Z: ZeroChoice> SharedKey<T, Z> {
214220

215221
// Check each non-constant coefficient
216222
for i in 1..self.point_polynomial.len() {
217-
// Update hash state with next coefficient
218-
hash_state = hash_state.add(self.point_polynomial[i]);
219-
220-
let hash_result = hash_state.clone().finalize_fixed();
221-
let hash_bytes: &[u8] = hash_result.as_ref();
222-
223223
let remaining_total = fingerprint
224224
.max_bits_total
225225
.saturating_sub(verified_bits as u8) as usize;
226226
if remaining_total == 0 {
227-
// We've verified enough bits total
228227
break;
229228
}
230229

230+
// Update hash state with next coefficient
231+
hash_state = hash_state.add(self.point_polynomial[i]);
232+
233+
let hash_result = hash_state.clone().finalize_fixed();
234+
let hash_bytes: &[u8] = hash_result.as_ref();
235+
231236
// NOTE: we don't get the zero bits and just substract it from the
232237
// total and move on -- we need to make sure each coefficient has at
233238
// least the required number of bits to make sure it really was part
@@ -236,13 +241,13 @@ impl<T: Normalized, Z: ZeroChoice> SharedKey<T, Z> {
236241
let actual_bits = Fingerprint::leading_zero_bits(hash_bytes);
237242

238243
if actual_bits < expected_bits {
239-
return false;
244+
return None;
240245
}
241246

242247
verified_bits += expected_bits;
243248
}
244249

245-
true
250+
Some(verified_bits)
246251
}
247252

248253
/// Grinds polynomial coefficients to embed the specified `fingerprint` through
@@ -594,7 +599,9 @@ mod test {
594599

595600
// Verify the fingerprint is valid
596601
assert!(
597-
shared_key.check_fingerprint::<sha2::Sha256>(fingerprint),
602+
shared_key
603+
.check_fingerprint::<sha2::Sha256>(fingerprint)
604+
.is_some(),
598605
"Grinded fingerprint should be valid"
599606
);
600607
}

0 commit comments

Comments
 (0)