@@ -83,15 +83,27 @@ SrsInitSrs::Response SrsInitSrs::execute(BB_UNUSED BBApiRequest& request) &&
8383 throw_or_abort (" SrsInitSrs: g1_points[1] does not match the canonical trusted-setup tau·G" );
8484 }
8585
86- // Defense in depth: hash-pin AND subgroup-check the G2 input. Hash equality alone is sufficient
87- // for the canonical case (it implies prime-order membership); the subgroup check is kept so
88- // that any future relaxation of the hash gate (e.g. a flag to allow a different trusted setup)
89- // does not silently reopen audit finding #7's small-subgroup attack.
86+ // Defense in depth: deserialize G2, then run three independent gates (infinity / hash / subgroup).
87+ //
88+ // Why all three:
89+ // - is_point_at_infinity(): `[x]_2 = O` is in G_r (so subgroup check passes) and matches no
90+ // canonical hash, but it would degenerate the KZG verifier — `e(−W, O) = 1` for every W,
91+ // so the second pairing in the opening check is identically 1 and a malicious prover can
92+ // forge openings for arbitrary statements. This must be rejected even if the hash gate is
93+ // ever loosened (e.g. a future "allow custom SRS" flag).
94+ // - SHA-256 vs BN254_G2_ELEMENT_SHA256: pins the exact canonical Aztec trusted-setup [x]_2,
95+ // blocking byte-level tampering and wrong-trusted-setup swaps.
96+ // - is_in_prime_subgroup(): rejects cofactor-subgroup points (audit finding #7's original
97+ // small-subgroup attack); strictly redundant given the hash pin but kept so that loosening
98+ // the hash gate does not silently reopen the cofactor attack either.
99+ auto g2_point_elem = from_buffer<g2::affine_element>(g2_point.data ());
100+ if (g2_point_elem.is_point_at_infinity ()) {
101+ throw_or_abort (" SrsInitSrs: g2_point cannot be the point at infinity" );
102+ }
90103 auto g2_hash = bb::crypto::sha256 (std::span<const uint8_t >(g2_point.data (), g2_point.size ()));
91104 if (g2_hash != bb::srs::BN254_G2_ELEMENT_SHA256) {
92105 throw_or_abort (" SrsInitSrs: g2_point bytes do not match the canonical Aztec [x]_2 SHA-256" );
93106 }
94- auto g2_point_elem = from_buffer<g2::affine_element>(g2_point.data ());
95107 if (!g2_point_elem.is_in_prime_subgroup ()) {
96108 throw_or_abort (" SrsInitSrs: g2_point is not in the BN254 G2 prime-order subgroup" );
97109 }
0 commit comments