Skip to content

fix(drive): verify root hash consistency in double-proof identity lookup#3341

Merged
QuantumExplorer merged 1 commit into
v3.1-devfrom
fix/proof-verifier-double-proof-root-hash
Mar 16, 2026
Merged

fix(drive): verify root hash consistency in double-proof identity lookup#3341
QuantumExplorer merged 1 commit into
v3.1-devfrom
fix/proof-verifier-double-proof-root-hash

Conversation

@QuantumExplorer

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Security audit discovered that the double-proof identity lookup (verify_full_identity_by_non_unique_public_key_hash) does not verify that both proofs come from the same tree state.

What was done?

The function uses a two-proof structure (acknowledged as "a hack" in the protobuf definition):

  1. First proof: maps public key hash → identity ID (root hash verified against quorum signature)
  2. Second proof: contains full identity data (root hash was silently discarded)

A malicious full node could serve a legitimate first proof but a stale or fabricated second proof containing manipulated identity data (different balance, different public keys, different revision). The light client would accept it as verified because only the first proof's root hash was checked against the quorum signature.

Fix

After verifying the second proof's Merkle consistency, compare its root hash against the first proof's root hash. If they differ, return ProofError::CorruptedProof. This ensures both proofs must come from the same quorum-signed tree state.

How Has This Been Tested?

All 3 existing tests pass:

cargo test -p drive --lib -- verify_full_identity_by_non_unique_public_key_hash
test result: ok. 3 passed; 0 failed; 0 ignored

The existing tests use proofs generated from the same Drive instance, so both proofs naturally have matching root hashes. The fix adds a guard that would reject proofs from different tree states.

Breaking Changes

None. Valid proofs from the same tree state will continue to verify. Only mismatched proofs (which indicate tampering) will now be rejected.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation if needed

🤖 Generated with Claude Code

In verify_full_identity_by_non_unique_public_key_hash_v0, the root hash
from the second proof (identity data) was silently discarded on line 76.
Only the first proof's root hash was validated against the quorum
signature. A malicious full node could serve a legitimate first proof
but a stale/historical second proof containing manipulated identity
data (different balance, keys, revision), and the light client would
accept it as verified.

Fix: compare the identity proof's root hash against the public key hash
proof's root hash. If they differ, return a CorruptedProof error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v3.1.0 milestone Mar 16, 2026
@coderabbitai

coderabbitai Bot commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@QuantumExplorer has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 22 minutes and 12 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0cdd0d4-829a-48f1-9cc8-dc3c6dd0d336

📥 Commits

Reviewing files that changed from the base of the PR and between 1284258 and 8189534.

📒 Files selected for processing (1)
  • packages/rs-drive/src/verify/identity/verify_full_identity_by_non_unique_public_key_hash/v0/mod.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/proof-verifier-double-proof-root-hash
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@QuantumExplorer QuantumExplorer left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Approved

@codecov

codecov Bot commented Mar 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.12%. Comparing base (e1404c1) to head (8189534).
⚠️ Report is 4 commits behind head on v3.1-dev.

Files with missing lines Patch % Lines
...l_identity_by_non_unique_public_key_hash/v0/mod.rs 66.66% 4 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           v3.1-dev    #3341      +/-   ##
============================================
- Coverage     74.12%   74.12%   -0.01%     
============================================
  Files          3296     3296              
  Lines        276765   276772       +7     
============================================
+ Hits         205163   205166       +3     
- Misses        71602    71606       +4     
Components Coverage Δ
dpp 60.42% <ø> (ø)
drive 80.23% <66.66%> (-0.01%) ⬇️
drive-abci 85.97% <ø> (ø)
sdk 31.25% <ø> (ø)
dapi-client 79.06% <ø> (ø)
platform-version ∅ <ø> (∅)
platform-value 58.46% <ø> (ø)
platform-wallet 60.40% <ø> (ø)
drive-proof-verifier ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@QuantumExplorer QuantumExplorer merged commit ed7fdda into v3.1-dev Mar 16, 2026
20 checks passed
@QuantumExplorer QuantumExplorer deleted the fix/proof-verifier-double-proof-root-hash branch March 16, 2026 06:43

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code Review

Correct security fix: the double-proof identity lookup was not verifying that both proofs share the same tree state root hash, allowing a malicious full node to serve stale/fabricated identity data in the second proof. The fix properly compares root hashes and rejects mismatches. The unique-key variants (verify_full_identity_by_unique_public_key_hash, verify_full_identities_by_public_key_hashes) are not affected because they use a single proof blob with in_subset = true, so both lookups inherently share the same root hash.

Reviewed commit: 8189534

🟡 1 suggestion(s)

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-drive/src/verify/identity/verify_full_identity_by_non_unique_public_key_hash/v0/mod.rs`:
- [SUGGESTION] lines 78-85: No test for mismatched root hash rejection
  The existing tests use proofs generated from the same Drive instance, so both proofs naturally have matching root hashes and always pass the new check. There is no test that verifies the new guard actually rejects mismatched root hashes.

A negative test would construct an `IdentityAndNonUniquePublicKeyHashDoubleProof` where `identity_proof` comes from a different Drive state than `identity_id_public_key_hash_proof`, then assert that verification returns `ProofError::CorruptedProof`. This is important because the entire point of this PR is catching this mismatch — without a test, a future refactor could regress it silently.

Comment on lines +78 to +85
// Both proofs must come from the same tree state. If the root
// hashes differ, the identity proof may be from a stale or
// fabricated state that does not match the quorum-signed root.
if identity_root_hash != root_hash {
return Err(Error::Proof(ProofError::CorruptedProof(
"identity proof root hash does not match the public key hash proof root hash".to_string(),
)));
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🟡 Suggestion: No test for mismatched root hash rejection

The existing tests use proofs generated from the same Drive instance, so both proofs naturally have matching root hashes and always pass the new check. There is no test that verifies the new guard actually rejects mismatched root hashes.

A negative test would construct an IdentityAndNonUniquePublicKeyHashDoubleProof where identity_proof comes from a different Drive state than identity_id_public_key_hash_proof, then assert that verification returns ProofError::CorruptedProof. This is important because the entire point of this PR is catching this mismatch — without a test, a future refactor could regress it silently.

source: ['coordinator']

🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-drive/src/verify/identity/verify_full_identity_by_non_unique_public_key_hash/v0/mod.rs`:
- [SUGGESTION] lines 78-85: No test for mismatched root hash rejection
  The existing tests use proofs generated from the same Drive instance, so both proofs naturally have matching root hashes and always pass the new check. There is no test that verifies the new guard actually rejects mismatched root hashes.

A negative test would construct an `IdentityAndNonUniquePublicKeyHashDoubleProof` where `identity_proof` comes from a different Drive state than `identity_id_public_key_hash_proof`, then assert that verification returns `ProofError::CorruptedProof`. This is important because the entire point of this PR is catching this mismatch — without a test, a future refactor could regress it silently.

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.

2 participants