From e4a737cc2198c4d95e8e8a29643f2e858ac5b5e7 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 21 Jan 2026 19:34:57 +0000 Subject: [PATCH 1/3] explicitly constrain inputs and intermediate witnesses --- .../dsl/acir_format/gate_count_constants.hpp | 2 +- .../stdlib/hash/sha256/sha256.cpp | 20 +++++++++++++++++-- .../stdlib/hash/sha256/sha256.test.cpp | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp index 9f51de59780c..d26d7851c191 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp @@ -27,7 +27,7 @@ template inline constexpr size_t BIG_QUAD = 2 + ZERO_GATE + M template inline constexpr size_t LOGIC_XOR_32 = 6 + ZERO_GATE + MEGA_OFFSET; template inline constexpr size_t LOGIC_AND_32 = 6 + ZERO_GATE + MEGA_OFFSET; template inline constexpr size_t RANGE_32 = 2744 + ZERO_GATE + MEGA_OFFSET; -template inline constexpr size_t SHA256_COMPRESSION = 6695 + ZERO_GATE + MEGA_OFFSET; +template inline constexpr size_t SHA256_COMPRESSION = 6704 + ZERO_GATE + MEGA_OFFSET; template inline constexpr size_t AES128_ENCRYPTION = 1432 + ZERO_GATE + MEGA_OFFSET; // The mega offset works differently for ECDSA opcodes because of the use of ROM tables, which use indices that diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp index a2bdb514e421..d7fb3f8b8b41 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp @@ -191,6 +191,15 @@ std::array, 64> SHA256::extend_witness(const std::arra w_sparse[i] = sparse_witness_limbs(w_out); } + /** + * Explicitly constrain w[62] and w[63] to 32 bits. All other computed w_out values + * (w[16..61]) are implicitly constrained via lookups in convert_witness(). + * + * While not strictly necessary for soundness, this simplifies security analysis at minimal cost. + */ + w_sparse[62].normal.create_range_constraint(32); + w_sparse[63].normal.create_range_constraint(32); + std::array w_extended; for (size_t i = 0; i < 64; ++i) { w_extended[i] = w_sparse[i].normal; @@ -370,8 +379,9 @@ field_t SHA256::add_normalize_unsafe(const field_t& a * This is the only public entry point for the stdlib SHA-256 implementation. We implement only the compression function * (rather than a full hash) because this is all that is required in DSL. * - * @note It is assumed that all 24 inputs (8 hash state + 16 message words) are 32-bit constrained externally so that - * the input has a unique representation. + * @note All 24 inputs (8 hash state + 16 message words) are 32-bit range constrained to ensure unique representation. + * Most are implicitly constrained via lookup tables; only h_init[3], h_init[7], and input[0] require explicit + * constraints as they are used purely in arithmetic operations. * * @param h_init The 8-word (256-bit) initial hash state. For the first block of a message, * this should be the standard SHA-256 IV. For subsequent blocks, this is the @@ -385,6 +395,12 @@ std::array, 8> SHA256::sha256_block(const std::array; + // Constrain inputs not implicitly constrained via lookups (h_init[3], h_init[7], input[0]). + // Other inputs are lookup-constrained in sparse form conversions or message extension. + h_init[3].create_range_constraint(32); + h_init[7].create_range_constraint(32); + input[0].create_range_constraint(32); + /** * Initialize round variables with previous block output. * Note: We delay converting `a` and `e` into their respective sparse forms because it's done as part of the diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp index 56f05a12802c..516b1bea6b83 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp @@ -87,7 +87,7 @@ TYPED_TEST(Sha256Test, BlockNistVectorOne) EXPECT_EQ(circuit_val, EXPECTED[i]) << "Circuit mismatch at index " << i; } - check_circuit_and_gate_count(builder, 6695); + check_circuit_and_gate_count(builder, 6704); EXPECT_EQ(builder.get_tables_size(), 35992); } @@ -165,7 +165,7 @@ TYPED_TEST(Sha256Test, BlockNistVectorTwo) EXPECT_EQ(circuit_val, EXPECTED[i]) << "Circuit mismatch at index " << i; } - check_circuit_and_gate_count(builder, 10633); + check_circuit_and_gate_count(builder, 10651); EXPECT_EQ(builder.get_tables_size(), 35992); } From 2c9ae12b459e401c182ede3522dffbd4cac361d2 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Thu, 22 Jan 2026 16:14:46 +0000 Subject: [PATCH 2/3] use direct range constraints --- .../dsl/acir_format/gate_count_constants.hpp | 2 +- .../stdlib/hash/sha256/sha256.cpp | 28 ++----------------- .../stdlib/hash/sha256/sha256.hpp | 2 -- .../stdlib/hash/sha256/sha256.test.cpp | 4 +-- 4 files changed, 5 insertions(+), 31 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp index d26d7851c191..fc58d69c87d3 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp @@ -27,7 +27,7 @@ template inline constexpr size_t BIG_QUAD = 2 + ZERO_GATE + M template inline constexpr size_t LOGIC_XOR_32 = 6 + ZERO_GATE + MEGA_OFFSET; template inline constexpr size_t LOGIC_AND_32 = 6 + ZERO_GATE + MEGA_OFFSET; template inline constexpr size_t RANGE_32 = 2744 + ZERO_GATE + MEGA_OFFSET; -template inline constexpr size_t SHA256_COMPRESSION = 6704 + ZERO_GATE + MEGA_OFFSET; +template inline constexpr size_t SHA256_COMPRESSION = 6702 + ZERO_GATE + MEGA_OFFSET; template inline constexpr size_t AES128_ENCRYPTION = 1432 + ZERO_GATE + MEGA_OFFSET; // The mega offset works differently for ECDSA opcodes because of the use of ROM tables, which use indices that diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp index d7fb3f8b8b41..4a1aa9cb1121 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp @@ -67,30 +67,6 @@ SHA256::sparse_witness_limbs SHA256::convert_witness(const fie return result; } -/** - * @brief Apply an implicit 32-bit range constraint by performing a lookup on the input. - * - * @details This is more efficient in the context of SHA-256 operations than explicit 32-bit range constraints since the - * lookup table is already in use. We use the SHA256_MAJ_INPUT MultiTable since it results in only 3 lookup gates per - * lookup. - * - * @note The result of the lookup is not used, but the accumulator outputs are marked as intentionally unused to - * avoid false positives in the boomerang value detection analysis. - * - * @param input The field element to constrain to 32 bits. - */ -template -void SHA256::apply_32_bit_range_constraint_via_lookup(const field_t& input) -{ - auto lookup_data = plookup_read::get_lookup_accumulators(MultiTableId::SHA256_MAJ_INPUT, input); - // Mark all accumulator outputs as intentionally unused (they exist only for the range constraint side-effect) - for (auto& col : lookup_data.columns) { - for (auto& elem : col) { - mark_witness_as_used(elem); - } - } -} - /** * @brief Extend the 16-word message block to 64 words per SHA-256 specification. * @@ -449,8 +425,8 @@ std::array, 8> SHA256::sha256_block(const std::array class SHA256 { static sparse_witness_limbs convert_witness(const field_ct& input); - static void apply_32_bit_range_constraint_via_lookup(const field_ct& input); - static field_ct choose_with_sigma1(sparse_value& e, const sparse_value& f, const sparse_value& g); static field_ct majority_with_sigma0(sparse_value& a, const sparse_value& b, const sparse_value& c); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp index 516b1bea6b83..64ec9e1706ef 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp @@ -87,7 +87,7 @@ TYPED_TEST(Sha256Test, BlockNistVectorOne) EXPECT_EQ(circuit_val, EXPECTED[i]) << "Circuit mismatch at index " << i; } - check_circuit_and_gate_count(builder, 6704); + check_circuit_and_gate_count(builder, 6702); EXPECT_EQ(builder.get_tables_size(), 35992); } @@ -165,7 +165,7 @@ TYPED_TEST(Sha256Test, BlockNistVectorTwo) EXPECT_EQ(circuit_val, EXPECTED[i]) << "Circuit mismatch at index " << i; } - check_circuit_and_gate_count(builder, 10651); + check_circuit_and_gate_count(builder, 10646); EXPECT_EQ(builder.get_tables_size(), 35992); } From 06112c7edffd8a5318797c3874094a101cc152d0 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Fri, 23 Jan 2026 21:05:33 +0000 Subject: [PATCH 3/3] vk hash --- .../cpp/scripts/test_chonk_standalone_vks_havent_changed.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh b/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh index 554805e35eea..29a2d87a43dd 100755 --- a/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh +++ b/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh @@ -13,7 +13,7 @@ cd .. # - Generate a hash for versioning: sha256sum bb-chonk-inputs.tar.gz # - Upload the compressed results: aws s3 cp bb-chonk-inputs.tar.gz s3://aztec-ci-artifacts/protocol/bb-chonk-inputs-[hash(0:8)].tar.gz # Note: In case of the "Test suite failed to run ... Unexpected token 'with' " error, need to run: docker pull aztecprotocol/build:3.0 -pinned_short_hash="f185b2ed" +pinned_short_hash="e3757861" pinned_chonk_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-chonk-inputs-${pinned_short_hash}.tar.gz" script_path="$(cd "$(dirname "${BASH_SOURCE[0]}")/scripts" && pwd)/$(basename "${BASH_SOURCE[0]}")"