Severity: P1/P2, ZKP batch bypass enabling private key share extraction
CMP Security Proof Citation
Per Canetti, Gennaro, Goldfeder, Makriyannis, Peled, “UC Non-Interactive, Proactive, Threshold ECDSA with Identifiable Aborts” (CCS 2021), Section 4.3, Theorem 4.1: the protocol’s simulation-based security REQUIRES that MTA range proofs provide soundness. Specifically, the UC security reduction assumes the adversary cannot submit values outside [-2^(l+eps), 2^(l+eps)] without being detected. The range proof soundness is the mechanism that enforces this bound.
CMP-2020 (ePrint 2020/492), Lemma 3: MTA security relies on the range proof preventing out-of-range values. If the range proof is bypassed, the adversary learns gamma * x_B + beta where x_B is the honest party’s key share and gamma is unbounded (attacker-controlled), directly leaking x_B.
This finding removes the range proof guarantee with practical probability (1/256), breaking the security assumption that the entire protocol relies on.
Summary
Type confusion: uint8_t gamma[16] instead of uint64_t gamma[2]. The & 0xffffffffff mask is a no-op on uint8_t. When gamma[0] = 0 (P=1/256), an invalid proof contributes E^0S^0=1 and 0(anything)=0 to the batch equation, completely invisible regardless of validity.
Location
- File:
src/common/cosigner/mta.cpp, lines 1099-1112 - Function:
batch_response_verifier::process_ring_pedersen() - Constants:
BATCH_STATISTICAL_SECURITY=5,MIN_BATCH_SIZE=6
Vulnerable Code
uint8_t gamma[2 * sizeof(uint64_t)]; // BUG: uint8_t[16] not uint64_t[2]
RAND_bytes(gamma, 2 * sizeof(uint64_t));
gamma[0] &= 0xffffffffff; // 40bits - NO-OP on uint8_t!
gamma[1] &= 0xffffffffff; // 40bits - NO-OP on uint8_t!
// Later:
BN_mul_word(tmp1, gamma[0]); // gamma[0] is uint8_t → 0-255 only
PoC Results (Verified, Forgery Proven)
All valid proofs: 200/200 pass ✓
5 valid + 1 INVALID, gamma=0 forced: 200/200 pass (ANY invalid proof invisible!)
5 valid + 1 INVALID, random 8-bit gamma: 7/2000 = 0.35% (≈1/256)
5 valid + 1 INVALID, gamma∈[1,255]: 0/2000 (never passes)
Attack Chain
- Malicious cosigner crafts MTA range proof with out-of-range value
- Honest verifier’s
batch_response_verifieruses_my_ring_pedersen gamma[0]= 0 with P=1/256 → invalid proof invisible- Out-of-range MTA accepted → honest party’s key share leaks
- ~256 signing sessions to extract key share
Remediation
uint64_t gamma[2];
RAND_bytes((uint8_t*)gamma, sizeof(gamma));
gamma[0] &= 0xffffffffffULL;
gamma[1] &= 0xffffffffffULL;
Source · github.com/zionsworking/security-research-notebook · writeups/fireblocks/03-mta-batch-verification-8bit-randomness-P2.md