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/bytecode/bc_hashing.pil
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ namespace bc_hashing;
pol commit sel_not_padding_2; // @boolean
sel_not_padding_1 * (1 - sel_not_padding_1) = 0;
sel_not_padding_2 * (1 - sel_not_padding_2) = 0;
// Force sel_not_padding_1 to 0 when sel is 0 (prevents ghost row injection attacks)
#[SEL_NOT_PADDING_1_REQUIRES_SEL]
sel_not_padding_1 * (1 - sel) = 0;
// Force sel_not_padding_2 to 0 when sel is 0 (prevents ghost row injection attacks)
#[SEL_NOT_PADDING_2_REQUIRES_SEL]
sel_not_padding_2 * (1 - sel) = 0;

// TODO: Instead of two bools, change to value = 0 (no padding) OR 1 (one field padding) OR 2 (two fields padding)?
// The annoyance is that we need many committed sels to use in/gate lookups.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -737,5 +737,36 @@ TEST_F(BytecodeHashingConstrainingTest, NegativeSingleBytecodeHashOutputConsiste
"Failed.*CHECK_FINAL_BYTES_REMAINING. Could not find tuple in destination.");
EXPECT_THROW_WITH_MESSAGE(check_relation<bc_hashing>(trace, bc_hashing::SR_ID_PROPAGATION), "ID_PROPAGATION");
}
// =====================================================================
// Ghost Row Injection Vulnerability Tests
// =====================================================================
// These tests verify that ghost rows (sel=0) cannot fire permutations.
// The fix: sel_not_padding_1 * (1 - sel) = 0 and sel_not_padding_2 * (1 - sel) = 0
// ensure these selectors are forced to 0 when sel=0.

TEST_F(BytecodeHashingConstrainingTest, NegativeGhostRowInjectionBlocked)
{
// Try to create a ghost row (sel=0) with sel_not_padding_1=1 or sel_not_padding_2=1
// which would fire the #[GET_PACKED_FIELD_1] or #[GET_PACKED_FIELD_2] permutations
TestTraceContainer trace({
{ { C::precomputed_first_row, 1 } },
{
{ C::bc_hashing_sel, 0 }, // Ghost row: gadget not active
{ C::bc_hashing_sel_not_padding_1, 1 }, // Try to fire permutation anyway
{ C::bc_hashing_sel_not_padding_2, 0 },
},
});

// The fix: sel_not_padding_1 * (1 - sel) = 0
EXPECT_THROW_WITH_MESSAGE(check_relation<bc_hashing>(trace), "SEL_NOT_PADDING_1_REQUIRES_SEL");

// Reset and try with sel_not_padding_2
trace.set(C::bc_hashing_sel_not_padding_1, 1, 0);
trace.set(C::bc_hashing_sel_not_padding_2, 1, 1);

// The fix: sel_not_padding_2 * (1 - sel) = 0
EXPECT_THROW_WITH_MESSAGE(check_relation<bc_hashing>(trace), "SEL_NOT_PADDING_2_REQUIRES_SEL");
}

} // namespace
} // namespace bb::avm2::constraining
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ template <typename FF_> class bc_hashingImpl {
public:
using FF = FF_;

static constexpr std::array<size_t, 22> SUBRELATION_PARTIAL_LENGTHS = { 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3,
3, 3, 3, 4, 4, 4, 4, 5, 3, 4, 3 };
static constexpr std::array<size_t, 24> SUBRELATION_PARTIAL_LENGTHS = { 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3,
3, 3, 3, 3, 4, 4, 4, 4, 5, 3, 4, 3 };

template <typename AllEntities> inline static bool skip(const AllEntities& in)
{
Expand Down Expand Up @@ -44,14 +44,16 @@ template <typename FF> class bc_hashing : public Relation<bc_hashingImpl<FF>> {
static constexpr size_t SR_PC_INCREMENTS_2 = 9;
static constexpr size_t SR_ID_PROPAGATION = 10;
static constexpr size_t SR_START_IS_SEPARATOR = 11;
static constexpr size_t SR_PADDING_CONSISTENCY = 14;
static constexpr size_t SR_PADDING_END = 15;
static constexpr size_t SR_PADDED_BY_ZERO_1 = 16;
static constexpr size_t SR_PADDED_BY_ZERO_2 = 17;
static constexpr size_t SR_PADDING_CORRECTNESS = 18;
static constexpr size_t SR_BYTECODE_LENGTH_FIELDS = 19;
static constexpr size_t SR_ROUNDS_DECREMENT = 20;
static constexpr size_t SR_HASH_IS_ID = 21;
static constexpr size_t SR_SEL_NOT_PADDING_1_REQUIRES_SEL = 14;
static constexpr size_t SR_SEL_NOT_PADDING_2_REQUIRES_SEL = 15;
static constexpr size_t SR_PADDING_CONSISTENCY = 16;
static constexpr size_t SR_PADDING_END = 17;
static constexpr size_t SR_PADDED_BY_ZERO_1 = 18;
static constexpr size_t SR_PADDED_BY_ZERO_2 = 19;
static constexpr size_t SR_PADDING_CORRECTNESS = 20;
static constexpr size_t SR_BYTECODE_LENGTH_FIELDS = 21;
static constexpr size_t SR_ROUNDS_DECREMENT = 22;
static constexpr size_t SR_HASH_IS_ID = 23;

static std::string get_subrelation_label(size_t index)
{
Expand All @@ -72,6 +74,10 @@ template <typename FF> class bc_hashing : public Relation<bc_hashingImpl<FF>> {
return "ID_PROPAGATION";
case SR_START_IS_SEPARATOR:
return "START_IS_SEPARATOR";
case SR_SEL_NOT_PADDING_1_REQUIRES_SEL:
return "SEL_NOT_PADDING_1_REQUIRES_SEL";
case SR_SEL_NOT_PADDING_2_REQUIRES_SEL:
return "SEL_NOT_PADDING_2_REQUIRES_SEL";
case SR_PADDING_CONSISTENCY:
return "PADDING_CONSISTENCY";
case SR_PADDING_END:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,61 +116,73 @@ void bc_hashingImpl<FF_>::accumulate(ContainerOverSubrelations& evals,
(FF(1) - static_cast<View>(in.get(C::bc_hashing_sel_not_padding_2)));
std::get<13>(evals) += (tmp * scaling_factor);
}
{ // PADDING_CONSISTENCY
{ // SEL_NOT_PADDING_1_REQUIRES_SEL
using View = typename std::tuple_element_t<14, ContainerOverSubrelations>::View;
auto tmp = CView(bc_hashing_PADDING_1) * static_cast<View>(in.get(C::bc_hashing_sel_not_padding_2));
auto tmp = static_cast<View>(in.get(C::bc_hashing_sel_not_padding_1)) *
(FF(1) - static_cast<View>(in.get(C::bc_hashing_sel)));
std::get<14>(evals) += (tmp * scaling_factor);
}
{ // PADDING_END
{ // SEL_NOT_PADDING_2_REQUIRES_SEL
using View = typename std::tuple_element_t<15, ContainerOverSubrelations>::View;
auto tmp = CView(bc_hashing_PADDING_2) * (FF(1) - static_cast<View>(in.get(C::bc_hashing_latch)));
auto tmp = static_cast<View>(in.get(C::bc_hashing_sel_not_padding_2)) *
(FF(1) - static_cast<View>(in.get(C::bc_hashing_sel)));
std::get<15>(evals) += (tmp * scaling_factor);
}
{ // PADDED_BY_ZERO_1
{ // PADDING_CONSISTENCY
using View = typename std::tuple_element_t<16, ContainerOverSubrelations>::View;
auto tmp = CView(bc_hashing_PADDING_1) * static_cast<View>(in.get(C::bc_hashing_packed_fields_1));
auto tmp = CView(bc_hashing_PADDING_1) * static_cast<View>(in.get(C::bc_hashing_sel_not_padding_2));
std::get<16>(evals) += (tmp * scaling_factor);
}
{ // PADDED_BY_ZERO_2
{ // PADDING_END
using View = typename std::tuple_element_t<17, ContainerOverSubrelations>::View;
auto tmp = CView(bc_hashing_PADDING_2) * static_cast<View>(in.get(C::bc_hashing_packed_fields_2));
auto tmp = CView(bc_hashing_PADDING_2) * (FF(1) - static_cast<View>(in.get(C::bc_hashing_latch)));
std::get<17>(evals) += (tmp * scaling_factor);
}
{ // PADDING_CORRECTNESS
{ // PADDED_BY_ZERO_1
using View = typename std::tuple_element_t<18, ContainerOverSubrelations>::View;
auto tmp = CView(bc_hashing_PADDING_1) * static_cast<View>(in.get(C::bc_hashing_packed_fields_1));
std::get<18>(evals) += (tmp * scaling_factor);
}
{ // PADDED_BY_ZERO_2
using View = typename std::tuple_element_t<19, ContainerOverSubrelations>::View;
auto tmp = CView(bc_hashing_PADDING_2) * static_cast<View>(in.get(C::bc_hashing_packed_fields_2));
std::get<19>(evals) += (tmp * scaling_factor);
}
{ // PADDING_CORRECTNESS
using View = typename std::tuple_element_t<20, ContainerOverSubrelations>::View;
auto tmp = (static_cast<View>(in.get(C::bc_hashing_pc_at_final_field)) -
static_cast<View>(in.get(C::bc_hashing_latch)) *
(CView(bc_hashing_PADDING_1) * static_cast<View>(in.get(C::bc_hashing_pc_index)) +
(CView(bc_hashing_PADDING_2) - CView(bc_hashing_PADDING_1)) *
static_cast<View>(in.get(C::bc_hashing_pc_index_1)) +
static_cast<View>(in.get(C::bc_hashing_sel_not_padding_2)) *
static_cast<View>(in.get(C::bc_hashing_pc_index_2))));
std::get<18>(evals) += (tmp * scaling_factor);
std::get<20>(evals) += (tmp * scaling_factor);
}
{ // BYTECODE_LENGTH_FIELDS
using View = typename std::tuple_element_t<19, ContainerOverSubrelations>::View;
using View = typename std::tuple_element_t<21, ContainerOverSubrelations>::View;
auto tmp = static_cast<View>(in.get(C::bc_hashing_latch)) *
(FF(31) * (static_cast<View>(in.get(C::bc_hashing_input_len)) - FF(2)) -
static_cast<View>(in.get(C::bc_hashing_pc_at_final_field)));
std::get<19>(evals) += (tmp * scaling_factor);
std::get<21>(evals) += (tmp * scaling_factor);
}
{ // ROUNDS_DECREMENT
using View = typename std::tuple_element_t<20, ContainerOverSubrelations>::View;
using View = typename std::tuple_element_t<22, ContainerOverSubrelations>::View;
auto tmp =
static_cast<View>(in.get(C::bc_hashing_sel)) *
((FF(1) - CView(bc_hashing_LATCH_CONDITION)) * ((static_cast<View>(in.get(C::bc_hashing_rounds_rem_shift)) -
static_cast<View>(in.get(C::bc_hashing_rounds_rem))) +
FF(1)) +
static_cast<View>(in.get(C::bc_hashing_latch)) *
(static_cast<View>(in.get(C::bc_hashing_rounds_rem)) - FF(1)));
std::get<20>(evals) += (tmp * scaling_factor);
std::get<22>(evals) += (tmp * scaling_factor);
}
{ // HASH_IS_ID
using View = typename std::tuple_element_t<21, ContainerOverSubrelations>::View;
using View = typename std::tuple_element_t<23, ContainerOverSubrelations>::View;
auto tmp =
static_cast<View>(in.get(C::bc_hashing_sel)) * (static_cast<View>(in.get(C::bc_hashing_bytecode_id)) -
static_cast<View>(in.get(C::bc_hashing_output_hash)));
std::get<21>(evals) += (tmp * scaling_factor);
std::get<23>(evals) += (tmp * scaling_factor);
}
}

Expand Down
Loading