Skip to content

Commit f2a1330

Browse files
fix: boomerang variable in sha256 hash function (#8581)
Static analyzer found boomerang variable in the function extend_witness. The problem is that variable w_out wasn't connected with variable w_out_raw in the function extend_witness in sha256 hash function. As a result, you can put random variable in the extend_witness. Test was created to prove this issue. You can modify a result of function extend_witness, and the circuit will be correct. Also function extend_witness was patched to remove this issue. --------- Co-authored-by: Rumata888 <isennovskiy@gmail.com>
1 parent 1c7b06d commit f2a1330

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed

barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class GoblinMockCircuits {
5656
{
5757
if (large) { // Results in circuit size 2^19
5858
stdlib::generate_sha256_test_circuit(builder, 12);
59-
stdlib::generate_ecdsa_verification_test_circuit(builder, 11);
59+
stdlib::generate_ecdsa_verification_test_circuit(builder, 10);
6060
stdlib::generate_merkle_membership_test_circuit(builder, 12);
6161
} else { // Results in circuit size 2^17
6262
stdlib::generate_sha256_test_circuit(builder, 9);

barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp

+36
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ using Builder = UltraCircuitBuilder;
2121
using byte_array_ct = byte_array<Builder>;
2222
using packed_byte_array_ct = packed_byte_array<Builder>;
2323
using field_ct = field_t<Builder>;
24+
using witness_ct = witness_t<Builder>;
2425

2526
constexpr uint64_t ror(uint64_t val, uint64_t shift)
2627
{
@@ -425,3 +426,38 @@ TEST(stdlib_sha256, test_input_str_len_multiple)
425426
EXPECT_EQ(circuit_output, expected);
426427
}
427428
}
429+
430+
TEST(stdlib_sha256, test_boomerang_value_regression)
431+
{
432+
auto builder = Builder();
433+
std::array<field_t<Builder>, 16> input;
434+
435+
// Create random input witnesses and ensure that the witnesses are constrained to constants
436+
for (size_t i = 0; i < 16; i++) {
437+
auto random32bits = engine.get_random_uint32();
438+
field_ct elt(witness_ct(&builder, fr(random32bits)));
439+
elt.fix_witness();
440+
input[i] = elt;
441+
}
442+
// Check correctness
443+
std::array<field_t<Builder>, 64> w_ext = sha256_plookup::extend_witness(input);
444+
bool result1 = CircuitChecker::check(builder);
445+
EXPECT_EQ(result1, true);
446+
bool result2 = false;
447+
for (auto& single_extended_witness : w_ext) {
448+
449+
auto random32bits = engine.get_random_uint32();
450+
uint32_t variable_index = single_extended_witness.witness_index;
451+
// Ensure our random value is different
452+
while (builder.variables[builder.real_variable_index[variable_index]] == fr(random32bits)) {
453+
random32bits = engine.get_random_uint32();
454+
}
455+
auto backup = builder.variables[builder.real_variable_index[variable_index]];
456+
builder.variables[builder.real_variable_index[variable_index]] = fr(random32bits);
457+
// Check that the circuit fails
458+
result2 = result2 || CircuitChecker::check(builder);
459+
builder.variables[builder.real_variable_index[variable_index]] = backup;
460+
}
461+
// If at least one of the updated witnesses hasn't caused the circuit to fail, we're in trouble
462+
EXPECT_EQ(result2, false);
463+
}

barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256_plookup.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,15 @@ std::array<field_t<Builder>, 64> extend_witness(const std::array<field_t<Builder
139139
} else {
140140
w_out = witness_t<Builder>(
141141
ctx, fr(w_out_raw.get_value().from_montgomery_form().data[0] & (uint64_t)0xffffffffULL));
142+
static constexpr fr inv_pow_two = fr(2).pow(32).invert();
143+
// If we multiply the field elements by constants separately and then subtract, then the divisor is going to
144+
// be in a normalized state right after subtraction and the call to .normalize() won't add gates
145+
field_pt w_out_raw_inv_pow_two = w_out_raw * inv_pow_two;
146+
field_pt w_out_inv_pow_two = w_out * inv_pow_two;
147+
field_pt divisor = (w_out_raw_inv_pow_two - w_out_inv_pow_two).normalize();
148+
ctx->create_new_range_constraint(divisor.witness_index, 3);
142149
}
150+
143151
w_sparse[i] = sparse_witness_limbs(w_out);
144152
}
145153

0 commit comments

Comments
 (0)