@@ -591,3 +591,84 @@ TEST_F(ChonkTests, ProofCompressionRoundtrip)
591591 // Verify the decompressed proof
592592 EXPECT_TRUE (verify_chonk (decompressed, vk_and_hash));
593593}
594+
595+ /* *
596+ * @brief Build a valid compressed proof, then corrupt a specific 32-byte element.
597+ */
598+ static std::vector<uint8_t > compress_and_corrupt (const ChonkProof& proof, size_t element_idx, const uint256_t & value)
599+ {
600+ auto compressed = ProofCompressor::compress_chonk_proof (proof);
601+ size_t byte_offset = element_idx * 32 ;
602+ EXPECT_LT (byte_offset + 32 , compressed.size ());
603+ // Overwrite the element with the given value (big-endian)
604+ std::vector<uint8_t > buf;
605+ write (buf, value);
606+ std::copy (buf.begin (), buf.end (), compressed.begin () + static_cast <ptrdiff_t >(byte_offset));
607+ return compressed;
608+ }
609+
610+ // Rejects a BN scalar value >= Fr::modulus
611+ TEST_F (ChonkTests, DecompressionRejectsNonCanonicalBN254Scalar)
612+ {
613+ TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
614+ auto [proof, vk_and_hash] = accumulate_and_prove_ivc (/* num_app_circuits=*/ 1 , settings);
615+ size_t mega_num_pub_inputs =
616+ proof.hiding_oink_proof .size () - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
617+ ASSERT_GT (mega_num_pub_inputs, 0 ); // Need at least one public input (BN254 scalar)
618+
619+ // Element 0 is the first public input (a BN254 scalar). Set it to Fr::modulus (non-canonical).
620+ using Fr = curve::BN254::ScalarField;
621+
622+ auto corrupted = compress_and_corrupt (proof, 0 , Fr::modulus);
623+ EXPECT_THROW_OR_ABORT (ProofCompressor::decompress_chonk_proof (corrupted, mega_num_pub_inputs), " " );
624+ }
625+
626+ // Rejects a BN commitment x-coordinate >= Fq::modulus
627+ TEST_F (ChonkTests, DecompressionRejectsNonCanonicalBN254CommitmentX)
628+ {
629+ using Fq = curve::BN254::BaseField;
630+
631+ TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
632+ auto [proof, vk_and_hash] = accumulate_and_prove_ivc (/* num_app_circuits=*/ 1 , settings);
633+ size_t mega_num_pub_inputs =
634+ proof.hiding_oink_proof .size () - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
635+
636+ // First BN254 commitment is at element index mega_num_pub_inputs.
637+ // Set x-coordinate to Fq::modulus + 1 (non-canonical, with no sign bit).
638+ auto corrupted = compress_and_corrupt (proof, mega_num_pub_inputs, Fq::modulus + 1 );
639+ EXPECT_THROW_OR_ABORT (ProofCompressor::decompress_chonk_proof (corrupted, mega_num_pub_inputs), " " );
640+ }
641+
642+ // Rejects a canonical x-coordinate that is not on the BN254 curve
643+ TEST_F (ChonkTests, DecompressionRejectsInvalidBN254CurvePoint)
644+ {
645+ using Fq = curve::BN254::BaseField;
646+
647+ TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
648+ auto [proof, vk_and_hash] = accumulate_and_prove_ivc (/* num_app_circuits=*/ 1 , settings);
649+ size_t mega_num_pub_inputs =
650+ proof.hiding_oink_proof .size () - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
651+
652+ // x=4 is not on BN254
653+ Fq x_not_on_curve (4 );
654+ Fq rhs = x_not_on_curve * x_not_on_curve * x_not_on_curve + Fq (3 );
655+ auto [is_sq, _] = rhs.sqrt ();
656+ ASSERT_FALSE (is_sq);
657+
658+ auto corrupted = compress_and_corrupt (proof, mega_num_pub_inputs, uint256_t (x_not_on_curve));
659+ EXPECT_THROW_OR_ABORT (ProofCompressor::decompress_chonk_proof (corrupted, mega_num_pub_inputs), " " );
660+ }
661+
662+ // Rejects a compressed proof that is too short (read_u256 bounds check)
663+ TEST_F (ChonkTests, DecompressionRejectsTruncatedProof)
664+ {
665+ TestSettings settings{ .log2_num_gates = SMALL_LOG_2_NUM_GATES };
666+ auto [proof, vk_and_hash] = accumulate_and_prove_ivc (/* num_app_circuits=*/ 1 , settings);
667+ size_t mega_num_pub_inputs =
668+ proof.hiding_oink_proof .size () - ProofLength::Oink<MegaZKFlavor>::LENGTH_WITHOUT_PUB_INPUTS;
669+
670+ auto compressed = ProofCompressor::compress_chonk_proof (proof);
671+ // Truncate by removing the last 32-byte element
672+ compressed.resize (compressed.size () - 32 );
673+ EXPECT_THROW_OR_ABORT (ProofCompressor::decompress_chonk_proof (compressed, mega_num_pub_inputs), " " );
674+ }
0 commit comments