Zion Boggan

In-depth vulnerability research, detection engineering & applied cryptography.

● Open to security-research & detection roles
GitHub · LinkedIn · Email
← Research notebook
Crypto soundness

Finding 03: MTA Batch Ring Pedersen Verification Uses 8-bit Randomness

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

  1. Malicious cosigner crafts MTA range proof with out-of-range value
  2. Honest verifier’s batch_response_verifier uses _my_ring_pedersen
  3. gamma[0] = 0 with P=1/256 → invalid proof invisible
  4. Out-of-range MTA accepted → honest party’s key share leaks
  5. ~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