Merge pull request #3215 from tnull/2024-08-protect-against-merkle-leaf-node-weakness

tx-sync: Protect against Core's Merkle leaf node weakness
This commit is contained in:
Matt Corallo 2024-08-08 16:10:18 +00:00 committed by GitHub
commit 77f8af07ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 28 additions and 0 deletions

View file

@ -270,6 +270,18 @@ where
for txid in &sync_state.watched_transactions {
match self.client.transaction_get(&txid) {
Ok(tx) => {
// Bitcoin Core's Merkle tree implementation has no way to discern between
// internal and leaf node entries. As a consequence it is susceptible to an
// attacker injecting additional transactions by crafting 64-byte
// transactions matching an inner Merkle node's hash (see
// https://web.archive.org/web/20240329003521/https://bitslog.com/2018/06/09/leaf-node-weakness-in-bitcoin-merkle-tree-design/).
// To protect against this (highly unlikely) attack vector, we check that the
// transaction is at least 65 bytes in length.
if tx.total_size() == 64 {
log_error!(self.logger, "Skipping transaction {} due to retrieving potentially invalid tx data.", txid);
continue;
}
watched_txs.push((txid, tx.clone()));
if let Some(tx_out) = tx.output.first() {
// We watch an arbitrary output of the transaction of interest in order to

View file

@ -372,6 +372,22 @@ where
return Err(InternalError::Failed);
}
// Bitcoin Core's Merkle tree implementation has no way to discern between
// internal and leaf node entries. As a consequence it is susceptible to an
// attacker injecting additional transactions by crafting 64-byte
// transactions matching an inner Merkle node's hash (see
// https://web.archive.org/web/20240329003521/https://bitslog.com/2018/06/09/leaf-node-weakness-in-bitcoin-merkle-tree-design/).
// To protect against this (highly unlikely) attack vector, we check that the
// transaction is at least 65 bytes in length.
if tx.total_size() == 64 {
log_error!(
self.logger,
"Skipping transaction {} due to retrieving potentially invalid tx data.",
txid
);
return Ok(None);
}
if let Some(block_height) = known_block_height {
// We can take a shortcut here if a previous call already gave us the height.
return Ok(Some(ConfirmedTx { tx, txid, block_header, pos, block_height }));