Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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]}")"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ template <typename Builder> inline constexpr size_t BIG_QUAD = 2 + ZERO_GATE + M
template <typename Builder> inline constexpr size_t LOGIC_XOR_32 = 6 + ZERO_GATE + MEGA_OFFSET<Builder>;
template <typename Builder> inline constexpr size_t LOGIC_AND_32 = 6 + ZERO_GATE + MEGA_OFFSET<Builder>;
template <typename Builder> inline constexpr size_t RANGE_32 = 2744 + ZERO_GATE + MEGA_OFFSET<Builder>;
template <typename Builder> inline constexpr size_t SHA256_COMPRESSION = 6695 + ZERO_GATE + MEGA_OFFSET<Builder>;
template <typename Builder> inline constexpr size_t SHA256_COMPRESSION = 6702 + ZERO_GATE + MEGA_OFFSET<Builder>;
template <typename Builder> inline constexpr size_t AES128_ENCRYPTION = 1432 + ZERO_GATE + MEGA_OFFSET<Builder>;

// The mega offset works differently for ECDSA opcodes because of the use of ROM tables, which use indices that
Expand Down
48 changes: 20 additions & 28 deletions barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,30 +67,6 @@ SHA256<Builder>::sparse_witness_limbs SHA256<Builder>::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 <typename Builder>
void SHA256<Builder>::apply_32_bit_range_constraint_via_lookup(const field_t<Builder>& input)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As Kesha pointed out, this is only beneficial if we don't already use the traditional range constraint mechanism in the circuit. In practice, noir applies explicit range constraints to all inputs so this will effectively never be the case. Thus, ignoring the high overhead, the cost is 1.75 vs 3 gates for explicit vs lookup-imposed range constraints.

{
auto lookup_data = plookup_read<Builder>::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.
*
Expand Down Expand Up @@ -191,6 +167,15 @@ std::array<field_t<Builder>, 64> SHA256<Builder>::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<field_pt, 64> w_extended;
for (size_t i = 0; i < 64; ++i) {
w_extended[i] = w_sparse[i].normal;
Expand Down Expand Up @@ -335,8 +320,9 @@ field_t<Builder> SHA256<Builder>::majority_with_sigma0(sparse_value& a, const sp
* 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
Expand All @@ -350,6 +336,12 @@ std::array<field_t<Builder>, 8> SHA256<Builder>::sha256_block(const std::array<f
{
using field_pt = field_t<Builder>;

// 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
Expand Down Expand Up @@ -400,8 +392,8 @@ std::array<field_t<Builder>, 8> SHA256<Builder>::sha256_block(const std::array<f
// Apply range constraints to `a` and `e` which are the only outputs of the previous loop not already
// lookup-constrained via sparse form conversion. Although not strictly necessary, this simplifies the analysis that
// the output of compression is fully constrained at minimal cost.
apply_32_bit_range_constraint_via_lookup(a.normal);
apply_32_bit_range_constraint_via_lookup(e.normal);
a.normal.create_range_constraint(32);
e.normal.create_range_constraint(32);

// Add round results into previous block output.
// Overflow bits = 1 since each summand is constrained to 32 bits.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,6 @@ template <typename Builder> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, 6702);
EXPECT_EQ(builder.get_tables_size(), 35992);
}

Expand Down Expand Up @@ -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, 10646);
EXPECT_EQ(builder.get_tables_size(), 35992);
}

Expand Down
Loading