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
6 changes: 6 additions & 0 deletions barretenberg/cpp/pil/vm2/sha256_mem.pil
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ namespace sha256;
#[CONTINUITY_OUTPUT_ADDR] // Propagate output addr down
(1 - LATCH_CONDITION) * (output_addr' - output_addr) = 0;

// Enforce input_addr increments by 1 during input rounds (first 16 rows)
// and stays constant otherwise. This prevents malicious provers from
// setting arbitrary memory addresses during SHA256 input loading.
#[CONTINUITY_INPUT_ADDR]
(1 - LATCH_CONDITION) * (input_addr' - (input_addr + sel_is_input_round)) = 0;

////////////////////////////////////////////////
// Error Handling - Out of Range Memory Access
////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -676,5 +676,67 @@ TEST(Sha256MemoryConstrainingTest, Complex)
check_all_interactions<Sha256TraceBuilder>(trace);
}

//////////////////////////////////////////
/// Negative Tests - Constraint Verification
//////////////////////////////////////////

// This test verifies that input_addr IS properly constrained on non-start rows.
//
// The sha256_mem.pil file propagates execution_clk, space_id, output_addr, and input_addr
// using CONTINUITY_* constraints. The CONTINUITY_INPUT_ADDR constraint ensures that
// input_addr increments by 1 during input rounds (when sel_is_input_round=1) and stays
// constant otherwise. This prevents malicious provers from reading arbitrary memory.
//
// APPROACH: Generate a valid SHA256 trace, then tamper with input_addr on a non-start row.
// The CONTINUITY_INPUT_ADDR constraint should catch this tampering and cause relation check to fail.
TEST(Sha256MemoryConstrainingTest, InputAddrTamperingIsCaughtByConstraint)
{
// Step 1: Generate a valid SHA256 compression trace using the actual simulation
MemoryStore mem;
StrictMock<MockExecutionIdManager> execution_id_manager;
EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
PureGreaterThan gt;
PureBitwise bitwise;

EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);

// Set up valid memory for state and input
std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
MemoryAddress state_addr = 0;
for (uint32_t i = 0; i < 8; ++i) {
mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
}

std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
MemoryAddress input_addr = 8; // Legitimate input address
for (uint32_t i = 0; i < 16; ++i) {
mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
}
MemoryAddress output_addr = 25;

// Execute SHA256 compression (this generates valid events)
sha256_gadget.compression(mem, state_addr, input_addr, output_addr);

// Build trace from events
TestTraceContainer trace;
trace.set(C::precomputed_first_row, 0, 1);
Sha256TraceBuilder builder;
builder.process(sha256_event_emitter.dump_events(), trace);

// Step 2: Verify the trace is valid BEFORE tampering
ASSERT_NO_THROW(check_relation<sha256_mem>(trace)) << "Trace should be valid before tampering";

// Step 3: Tamper with input_addr on row 1 (a non-start input round)
// The constraint should catch this because input_addr[1] must equal input_addr[0] + 1
constexpr uint32_t MALICIOUS_ADDR = 9999;
trace.set(C::sha256_input_addr, 1, MALICIOUS_ADDR);

// Step 4: Verify the CONTINUITY_INPUT_ADDR constraint catches the tampering
// The constraint enforces: input_addr' = input_addr + sel_is_input_round
EXPECT_THROW_WITH_MESSAGE(check_relation<sha256_mem>(trace, sha256_mem::SR_CONTINUITY_INPUT_ADDR),
"CONTINUITY_INPUT_ADDR");
}

} // namespace
} // namespace bb::avm2::constraining
14 changes: 7 additions & 7 deletions barretenberg/cpp/src/barretenberg/vm2/generated/columns.hpp

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ namespace bb::avm2 {
struct AvmFlavorVariables {
static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 123;
static constexpr size_t NUM_WITNESS_ENTITIES = 3059;
static constexpr size_t NUM_SHIFTED_ENTITIES = 344;
static constexpr size_t NUM_SHIFTED_ENTITIES = 345;
static constexpr size_t NUM_WIRES = 2593;
static constexpr size_t NUM_ALL_ENTITIES = 3526;
static constexpr size_t NUM_ALL_ENTITIES = 3527;

// Need to be templated for recursive verifier
template <typename FF_>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ template <typename FF_> class sha256_memImpl {
public:
using FF = FF_;

static constexpr std::array<size_t, 51> SUBRELATION_PARTIAL_LENGTHS = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 2, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 3, 3, 6, 4, 3, 3, 5,
4, 3, 4, 5, 3, 3, 3, 5, 6, 3, 3, 4 };
static constexpr std::array<size_t, 52> SUBRELATION_PARTIAL_LENGTHS = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 4, 2, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 3, 3, 6, 4, 3, 3,
5, 4, 3, 4, 5, 3, 3, 3, 5, 6, 3, 3, 4 };

template <typename AllEntities> inline static bool skip(const AllEntities& in)
{
Expand All @@ -44,17 +44,18 @@ template <typename FF> class sha256_mem : public Relation<sha256_memImpl<FF>> {
static constexpr size_t SR_CONTINUITY_EXEC_CLK = 6;
static constexpr size_t SR_CONTINUITY_SPACE_ID = 7;
static constexpr size_t SR_CONTINUITY_OUTPUT_ADDR = 8;
static constexpr size_t SR_START_OR_LAST_MEM = 15;
static constexpr size_t SR_BATCH_ZERO_CHECK_READ = 34;
static constexpr size_t SR_BATCH_ENFORCE_ZERO_WRITE = 35;
static constexpr size_t SR_SEL_IS_INPUT_ROUND_START_COND = 37;
static constexpr size_t SR_SEL_IS_INPUT_END = 38;
static constexpr size_t SR_SEL_IS_INPUT_PROPAGATE = 39;
static constexpr size_t SR_INPUT_ROUND_CTR_START_COND = 40;
static constexpr size_t SR_INPUT_ROUND_CTR_DECR_COND = 41;
static constexpr size_t SR_INPUT_TAG_DIFF_CHECK = 46;
static constexpr size_t SR_TAG_ERROR_INIT = 48;
static constexpr size_t SR_TAG_ERROR_PROPAGATION = 49;
static constexpr size_t SR_CONTINUITY_INPUT_ADDR = 9;
static constexpr size_t SR_START_OR_LAST_MEM = 16;
static constexpr size_t SR_BATCH_ZERO_CHECK_READ = 35;
static constexpr size_t SR_BATCH_ENFORCE_ZERO_WRITE = 36;
static constexpr size_t SR_SEL_IS_INPUT_ROUND_START_COND = 38;
static constexpr size_t SR_SEL_IS_INPUT_END = 39;
static constexpr size_t SR_SEL_IS_INPUT_PROPAGATE = 40;
static constexpr size_t SR_INPUT_ROUND_CTR_START_COND = 41;
static constexpr size_t SR_INPUT_ROUND_CTR_DECR_COND = 42;
static constexpr size_t SR_INPUT_TAG_DIFF_CHECK = 47;
static constexpr size_t SR_TAG_ERROR_INIT = 49;
static constexpr size_t SR_TAG_ERROR_PROPAGATION = 50;

static std::string get_subrelation_label(size_t index)
{
Expand All @@ -71,6 +72,8 @@ template <typename FF> class sha256_mem : public Relation<sha256_memImpl<FF>> {
return "CONTINUITY_SPACE_ID";
case SR_CONTINUITY_OUTPUT_ADDR:
return "CONTINUITY_OUTPUT_ADDR";
case SR_CONTINUITY_INPUT_ADDR:
return "CONTINUITY_INPUT_ADDR";
case SR_START_OR_LAST_MEM:
return "START_OR_LAST_MEM";
case SR_BATCH_ZERO_CHECK_READ:
Expand Down
Loading
Loading