Skip to content

test: proof forgery exploit tests for KVValueHash and KVRefValueHash#630

Merged
QuantumExplorer merged 1 commit into
developfrom
test/kvrefvaluehash-exploit
Mar 9, 2026
Merged

test: proof forgery exploit tests for KVValueHash and KVRefValueHash#630
QuantumExplorer merged 1 commit into
developfrom
test/kvrefvaluehash-exploit

Conversation

@QuantumExplorer
Copy link
Copy Markdown
Member

@QuantumExplorer QuantumExplorer commented Mar 9, 2026

Summary

Details

KV→KVValueHash (vulnerable): An attacker replaces a KV node (tag 0x03) with KVValueHash (tag 0x04), keeping the real value_hash but injecting a fake value. The merk verifier uses the provided value_hash directly in the tree hash, so the root hash is unchanged. The fake value passes through to the GroveDB result set.

KV→KVRefValueHash (NOT vulnerable): The same attack fails because KVRefValueHash computes its tree hash as:

kv_hash(key, combine_hash(node_value_hash, H(referenced_value)))

The H(referenced_value) term means changing the value changes the tree hash, breaking root hash verification. This is a structural defense — no additional check is needed.

Test plan

  • kv_to_kvvaluehash_forgery_exploit — confirms exploit succeeds on develop
  • kv_to_kvrefvaluehash_forgery_exploit — confirms exploit fails for KVRefValueHash

Note: kv_to_kvvaluehash_forgery_exploit will need to be updated when PR #553 merges (the exploit will be blocked).

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Tests
    • Expanded proof verification test coverage with new security scenarios that validate the system's resilience to Merkle proof tampering exploits. Added test helpers to assess how proof verification handles malicious modifications to proof structures.

…ValueHash

Adds two tests demonstrating the KV→KVValueHash proof forgery attack vector
and verifying that KV→KVRefValueHash is NOT exploitable.

- kv_to_kvvaluehash_forgery_exploit: Confirms that replacing a KV node with
  KVValueHash (keeping the real value_hash but injecting a fake value) succeeds
  on the current codebase. The fix for this is in PR #553.

- kv_to_kvrefvaluehash_forgery_exploit: Confirms that the same attack with
  KVRefValueHash fails because KVRefValueHash incorporates H(value) into the
  tree hash via combine_hash(node_value_hash, H(referenced_value)). Changing
  the value changes the root hash, so the forgery is detected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 9, 2026

📝 Walkthrough

Walkthrough

Adds two new forgery-exploit tests to proof_coverage_tests.rs that tamper with Merkle proofs by substituting KV nodes with KVValueHash and KVRefValueHash nodes, along with helper functions to perform these tampering operations. Verifies proof verification behavior against these specific node substitution attacks.

Changes

Cohort / File(s) Summary
Proof Tampering Tests
grovedb/src/tests/proof_coverage_tests.rs
Adds two new forgery-exploit tests (tamper_kv_to_kvvaluehash and tamper_kv_to_kvrefvaluehash) that generate valid proofs, tamper with KV nodes by replacing them with KVValueHash (tag 0x04) or KVRefValueHash (tag 0x06) nodes, and verify proof verifier behavior against these substitutions. Includes helper functions to locate and replace targeted KV leaf nodes within merkle proofs and rebuild tampered proof structures.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 New tests hop into the fold,
With tampering tricks and proofs so bold,
We forge and check with whisker-twitching care,
To catch the KV tricks floating in the air!
⬜⬜⬜

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and accurately describes the main change: adding proof forgery exploit tests for two specific node types (KVValueHash and KVRefValueHash).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test/kvrefvaluehash-exploit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@grovedb/src/tests/proof_coverage_tests.rs`:
- Around line 6013-6014: The tests call GroveDb::verify_query_raw which returns
a recomputed root but does not assert it matches the DB's trusted root; update
the exploit tests (e.g., KVValueHash and KVRefValueHash) to fetch the actual
trusted root via db.root_hash(grove_version) and assert the recomputed root
equals that trusted root (instead of only checking valid_result.is_ok()). Locate
the verification call using GroveDb::verify_query_raw(&proof_bytes, &path_query,
grove_version) and replace or augment the assert to compare the returned root to
db.root_hash(...) so the tests fail when tampering changes the root; apply the
same change to the similar test blocks referenced (around the other ranges) to
ensure all exploit tests check the trusted root.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81da2760-670c-42dd-8e8e-cc40f071e1d7

📥 Commits

Reviewing files that changed from the base of the PR and between 49d95fd and b032869.

📒 Files selected for processing (1)
  • grovedb/src/tests/proof_coverage_tests.rs

Comment on lines +6013 to +6014
let valid_result = GroveDb::verify_query_raw(&proof_bytes, &path_query, grove_version);
assert!(valid_result.is_ok(), "valid proof should verify");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Check the trusted root hash in both exploit tests.

verify_query_raw returns a recomputed root; it does not prove the tampered proof still matches the original DB root. As written, the KVValueHash test can pass even if tampering changes the root, and the KVRefValueHash test can fail even when the safe behavior is just “different root, still structurally decodable.” Assert against db.root_hash(...) in both cases.

🛠️ Suggested fix
         let proof_bytes = db
             .prove_query(&path_query, None, grove_version)
             .unwrap()
             .expect("should generate proof");

-        let valid_result = GroveDb::verify_query_raw(&proof_bytes, &path_query, grove_version);
-        assert!(valid_result.is_ok(), "valid proof should verify");
+        let expected_root = db.root_hash(None, grove_version).unwrap().unwrap();
+        let (valid_root, _) =
+            GroveDb::verify_query_raw(&proof_bytes, &path_query, grove_version)
+                .expect("valid proof should verify");
+        assert_eq!(valid_root, expected_root, "valid proof should reproduce trusted root");

         let real_element_bytes = Element::new_item(real_value.clone())
             .serialize(grove_version)
             .unwrap();
@@
         let tampered_result =
             GroveDb::verify_query_raw(&tampered_proof_bytes, &path_query, grove_version);

         // On develop (no fix), this exploit SUCCEEDS — the verifier accepts the
         // fake value. The fix (on the PR branch) rejects KVValueHash nodes
         // containing item elements at the merk verification level.
         match tampered_result {
-            Ok((_, results)) => {
+            Ok((tampered_root, results)) => {
+                assert_eq!(
+                    tampered_root, expected_root,
+                    "KV→KVValueHash is only a real forgery if the trusted root is unchanged"
+                );
                 let got_fake = results.iter().any(|pkv| {
                     let element = Element::deserialize(&pkv.value, grove_version);
                     matches!(element, Ok(Element::Item(v, _)) if v == fake_value)
                 });
                 assert!(
@@
         let proof_bytes = db
             .prove_query(&path_query, None, grove_version)
             .unwrap()
             .expect("should generate proof");

-        // Sanity: the valid proof verifies correctly
-        let valid_result = GroveDb::verify_query_raw(&proof_bytes, &path_query, grove_version);
-        assert!(valid_result.is_ok(), "valid proof should verify");
+        let expected_root = db.root_hash(None, grove_version).unwrap().unwrap();
+        let (valid_root, _) =
+            GroveDb::verify_query_raw(&proof_bytes, &path_query, grove_version)
+                .expect("valid proof should verify");
+        assert_eq!(valid_root, expected_root, "valid proof should reproduce trusted root");
@@
         let tampered_result =
             GroveDb::verify_query_raw(&tampered_proof_bytes, &path_query, grove_version);

-        assert!(
-            tampered_result.is_err(),
-            "KV→KVRefValueHash forgery should be rejected because \
-             KVRefValueHash incorporates H(value) into the tree hash via \
-             combine_hash, so changing the value changes the root hash"
-        );
+        match tampered_result {
+            Ok((tampered_root, _)) => assert_ne!(
+                tampered_root, expected_root,
+                "KV→KVRefValueHash should change the recomputed root hash"
+            ),
+            Err(_) => {}
+        }

Also applies to: 6065-6091, 6202-6204, 6266-6274

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@grovedb/src/tests/proof_coverage_tests.rs` around lines 6013 - 6014, The
tests call GroveDb::verify_query_raw which returns a recomputed root but does
not assert it matches the DB's trusted root; update the exploit tests (e.g.,
KVValueHash and KVRefValueHash) to fetch the actual trusted root via
db.root_hash(grove_version) and assert the recomputed root equals that trusted
root (instead of only checking valid_result.is_ok()). Locate the verification
call using GroveDb::verify_query_raw(&proof_bytes, &path_query, grove_version)
and replace or augment the assert to compare the returned root to
db.root_hash(...) so the tests fail when tampering changes the root; apply the
same change to the similar test blocks referenced (around the other ranges) to
ensure all exploit tests check the trusted root.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.75%. Comparing base (6285dde) to head (b032869).
⚠️ Report is 17 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #630      +/-   ##
===========================================
+ Coverage    90.70%   90.75%   +0.05%     
===========================================
  Files          182      182              
  Lines        50734    51082     +348     
===========================================
+ Hits         46016    46358     +342     
- Misses        4718     4724       +6     
Components Coverage Δ
grovedb-core 88.92% <ø> (+0.11%) ⬆️
merk 92.05% <ø> (-0.01%) ⬇️
storage 86.36% <ø> (-0.01%) ⬇️
commitment-tree 96.41% <ø> (ø)
mmr 96.72% <ø> (ø)
bulk-append-tree 90.85% <ø> (ø)
element 97.55% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@QuantumExplorer QuantumExplorer merged commit 319dea5 into develop Mar 9, 2026
10 checks passed
@QuantumExplorer QuantumExplorer deleted the test/kvrefvaluehash-exploit branch March 9, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant