Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
tamper_proof.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#pragma once
8
12#include <cstddef>
13namespace bb {
14enum class TamperType {
15 MODIFY_SUMCHECK_UNIVARIATE, // Tamper with coefficients of a Sumcheck Round Univariate
16 MODIFY_SUMCHECK_EVAL, // Tamper with a multilinear evaluation of an entity
17 MODIFY_Z_PERM_COMMITMENT, // Tamper with the commitment to z_perm
18 MODIFY_GEMINI_WITNESS, // Tamper with a fold polynomial
19 END
20};
21
34template <typename InnerProver, typename InnerFlavor, typename ProofType>
35void tamper_with_proof(InnerProver& inner_prover, ProofType& inner_proof, TamperType type)
36{
37 using InnerFF = typename InnerFlavor::FF;
38 static constexpr size_t FIRST_WITNESS_INDEX = InnerFlavor::NUM_PRECOMPUTED_ENTITIES;
39
40 // Deserialize the transcript into the struct so that we can tamper it
41 auto num_public_inputs = inner_prover.prover_instance->num_public_inputs();
42 inner_prover.transcript->deserialize_full_transcript(num_public_inputs);
43
44 switch (type) {
45
47 InnerFF random_value = InnerFF::random_element();
48 // Preserve the S_0(0) + S_0(1) = target_total_sum = 0, but the check S_0(u_0) = S_1(0) + S_1(1) would fail whp.
49 // The branching is due to the Flavor structure.
50 if constexpr (!InnerFlavor::HasZK) {
51 inner_prover.transcript->sumcheck_univariates[0].value_at(0) += random_value;
52 inner_prover.transcript->sumcheck_univariates[0].value_at(1) -= random_value;
53 } else {
54 inner_prover.transcript->zk_sumcheck_univariates[0].value_at(0) += random_value;
55 inner_prover.transcript->zk_sumcheck_univariates[0].value_at(1) -= random_value;
56 }
57 break;
58 }
59
61 // Corrupt the evaluation of the first witness. Captures that the check full_honk_purported_value =
62 // round.target_total_sum is performed in-circuit.
63 inner_prover.transcript->sumcheck_evaluations[FIRST_WITNESS_INDEX] = InnerFF::random_element();
64 break;
65
67 // Tamper with the commitment to z_perm.
68 inner_prover.transcript->z_perm_comm = inner_prover.transcript->z_perm_comm * InnerFF::random_element();
69 break;
70
72 InnerFF random_scalar = InnerFF::random_element();
73 // Tamper with the first fold commitment. In non-ZK cases, could only be captured by the pairing check.
74 inner_prover.transcript->gemini_fold_comms[0] = inner_prover.transcript->gemini_fold_comms[0] * random_scalar;
75 inner_prover.transcript->gemini_fold_evals[0] *= 0;
76 break;
77 }
78 case TamperType::END: {
79 break;
80 }
81 }
82
83 // Serialize transcript
84 // As inner_proof is extracted with export_proof, the internal values of inner_prover.transcript are reset
85 // Therefore, if we were to call export_proof without overriding num_frs_written and proof_start, the proof would
86 // be empty. This is a hack, we should probably have a better way of tampering with proofs.
87 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1411) Use std::unordered map in Transcript so that we
88 // can access/modify elements of a proof more easily
89 inner_prover.transcript->serialize_full_transcript();
90 size_t num_frs = InnerFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS() + num_public_inputs;
92 // Exclude the IPA points from the proof - they are added again by export_proof
93 num_frs -= IPA_PROOF_LENGTH;
94 }
95 inner_prover.transcript->test_set_proof_parsing_state(0, num_frs);
96
97 // Extract the tampered proof
98 inner_proof = inner_prover.export_proof();
99}
100
106template <typename InnerProver, typename InnerFlavor, typename ProofType>
107void tamper_with_proof(ProofType& inner_proof, bool end_of_proof)
108{
109 using Commitment = typename InnerFlavor::Curve::AffineElement;
110 using FF = typename InnerFlavor::FF;
111 using ProofFF = typename ProofType::value_type;
112
113 if (!end_of_proof) {
114 // Tamper with the first pairing point (P0) by adding the generator
115 // The number of field elements per point depends on the curve:
116 // - BN254: 8 field elements (4 limbs per coordinate)
117 // - Grumpkin: 2 field elements (1 per coordinate)
118 constexpr size_t FRS_PER_POINT = Commitment::PUBLIC_INPUTS_SIZE;
119 constexpr size_t NUM_LIMB_BITS = bb::stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION;
120
121 if (inner_proof.size() >= FRS_PER_POINT) {
122 // Deserialize P0 from proof using the native reconstruct_from_public method
124 std::copy_n(inner_proof.begin(), FRS_PER_POINT, p0_limbs.begin());
125 Commitment P0 = Commitment::reconstruct_from_public(p0_limbs);
126
127 // Tamper: P0 + G (still on curve, but invalid for verification)
128 Commitment tampered_point = P0 + Commitment::one();
129
130 // Manually serialize tampered point back to proof based on curve type
131 if constexpr (FRS_PER_POINT == 8) {
132 // BN254: Serialize using bigfield representation (4 limbs of 68 bits each per coordinate)
133 constexpr uint256_t LIMB_MASK = (uint256_t(1) << NUM_LIMB_BITS) - 1;
134
135 uint256_t x_val = uint256_t(tampered_point.x);
136 inner_proof[0] = ProofFF(x_val & LIMB_MASK);
137 inner_proof[1] = ProofFF((x_val >> NUM_LIMB_BITS) & LIMB_MASK);
138 inner_proof[2] = ProofFF((x_val >> (2 * NUM_LIMB_BITS)) & LIMB_MASK);
139 inner_proof[3] = ProofFF((x_val >> (3 * NUM_LIMB_BITS)) & LIMB_MASK);
140
141 uint256_t y_val = uint256_t(tampered_point.y);
142 inner_proof[4] = ProofFF(y_val & LIMB_MASK);
143 inner_proof[5] = ProofFF((y_val >> NUM_LIMB_BITS) & LIMB_MASK);
144 inner_proof[6] = ProofFF((y_val >> (2 * NUM_LIMB_BITS)) & LIMB_MASK);
145 inner_proof[7] = ProofFF((y_val >> (3 * NUM_LIMB_BITS)) & LIMB_MASK);
146 } else if constexpr (FRS_PER_POINT == 2) {
147 // Grumpkin: Serialize directly (1 field element per coordinate)
148 inner_proof[0] = ProofFF(tampered_point.x);
149 inner_proof[1] = ProofFF(tampered_point.y);
150 } else {
151 static_assert(FRS_PER_POINT == 8 || FRS_PER_POINT == 2,
152 "Unsupported curve: FRS_PER_POINT must be 8 (BN254) or 2 (Grumpkin)");
153 }
154 }
155 } else {
156 // Manually deserialize, modify, and serialize the last commitment contained in the proof.
157 static constexpr size_t num_frs_comm = FrCodec::calc_num_fields<Commitment>();
158 size_t offset = inner_proof.size() - num_frs_comm;
159
160 auto element_frs = std::span{ inner_proof }.subspan(offset, num_frs_comm);
161 auto last_commitment = NativeTranscript::deserialize<Commitment>(element_frs);
162 last_commitment = last_commitment * FF(2);
163 auto last_commitment_reserialized = NativeTranscript::serialize(last_commitment);
164 std::copy(last_commitment_reserialized.begin(),
165 last_commitment_reserialized.end(),
166 inner_proof.begin() + static_cast<std::ptrdiff_t>(offset));
167 }
168}
169} // namespace bb
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:22
static std::vector< DataType > serialize(const T &element)
ssize_t offset
Definition engine.cpp:36
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
@ MODIFY_SUMCHECK_UNIVARIATE
void tamper_with_proof(InnerProver &inner_prover, ProofType &inner_proof, TamperType type)
Test method that provides several ways to tamper with a proof. TODO(https://github....
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13