Data hash in return value of prove_next_header_data_commitment
underconstrained
Description
The prove_next_header_data_commitment
takes trusted inputs prev_header_hash
and prev_block_number
and assumes that prev_header_hash
is the Merkle root hash of the block at height prev_block_number
. It is intended to return the Merkle root hash of a Merkle tree containing a sole leaf abi.encode(prev_block_number, DataHash)
, where the second argument is the DataHash
field of the block with header Merkle-root hash prev_header_hash
.
However, while the DataHash
field associated to prev_header_hash
is constrained to be the protobuf-decoding of data_comm_proof.data_hash_proofs[0].leaf
, an unconstrained witness data_comm_proof.data_hashes[0]
is instead used when constructing the return value.
A similar oversight is also present in prove_subchain
.
The prove_subchain
function returns a MapReduceSubchainVariable
containing the Merkle-root hash of the batches' data commitments, calculated as follows:
let data_merkle_root = self.get_data_commitment::<BATCH_SIZE>(
&data_comm_proof.data_hashes,
batch_start_block,
end_block_num,
);
The leaf data used here is data_comm_proof.data_hashes
, which is, however, completely unconstrained.
Elsewhere in the function, a Merkle inclusion proof for the DataHash
field of the enabled Tendermint blocks is checked:
let data_hash_proof_root = self
.get_root_from_merkle_proof::<HEADER_PROOF_DEPTH, PROTOBUF_HASH_SIZE_BYTES>(
&data_comm_proof.data_hash_proofs[i],
&data_hash_path,
);
Later verification of data_hash_proof_root
for enabled blocks shows that data_comm_proof.data_hash_proofs[i].leaf
is the protobuf encoding of the DataHash
field of the associated Tendermint block. However, there is no check to ensure that data_comm_proof.data_hashes[i]
is the protobuf decoding of data_comm_proof.data_hash_proofs[i].leaf
.
Impact
An attacker could generate a proof with arbitrary data hashes of their choosing in the data commitments.
Recommendations
Consider using data_comm_proof.data_hash_proofs[i].leaf
instead of data_comm_proof.data_hashes[i]
to construct the data commitments. Note that data_comm_proof.data_hash_proofs[i].leaf
is the protobuf encoding of the DataHash
field, so it consists of two bytes of protobuf header followed by the actual 32 bytes of the DataHash
field.
Remediation
This issue has been acknowledged by Succinct, and a fix was implemented in the following commit: .