Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: check the namespace ID range of the leafHash in an absence proof to ensure soundness #116

Merged
merged 3 commits into from
Mar 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ func (proof Proof) VerifyNamespace(h hash.Hash, nID namespace.ID, data [][]byte,
nIDLen := nID.Size()
if proof.IsOfAbsence() {
gotLeafHashes = append(gotLeafHashes, proof.leafHash)
// conduct some sanity checks:
leafMinNID := namespace.ID(proof.leafHash[:nIDLen])
if !nID.Less(leafMinNID) {
// leafHash.minNID must be greater than nID
return false
}
rootulp marked this conversation as resolved.
Show resolved Hide resolved

} else {
// collect leaf hashes from provided data and do some sanity checks:
hashLeafFunc := nth.HashLeaf
Expand Down
13 changes: 13 additions & 0 deletions proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ func TestProof_VerifyNamespace_False(t *testing.T) {
}
pushedZeroNs := n.Get([]byte{0, 0, 0})
pushedLastNs := n.Get([]byte{0, 0, 8})

// an invalid absence proof for an existing namespace ID (2) in the constructed tree
leafIndex := 3
inclusionProofOfLeafIndex := rangeProof(t, n, leafIndex, leafIndex+1)
n.computeLeafHashesIfNecessary()
leafHash := n.leafHashes[leafIndex] // the only data item with namespace ID = 2 in the constructed tree is at index 3
invalidAbsenceProof := NewAbsenceProof(leafIndex, leafIndex+1, inclusionProofOfLeafIndex, leafHash, false)

tests := []struct {
name string
proof Proof
Expand Down Expand Up @@ -134,6 +142,11 @@ func TestProof_VerifyNamespace_False(t *testing.T) {
args{[]byte{0, 0, 0}, pushedZeroNs[:len(pushedZeroNs)-2], n.Root()},
false,
},
{
"invalid absence proof of an existing nid", invalidAbsenceProof,
args{[]byte{0, 0, 2}, [][]byte{}, n.Root()},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
7 changes: 4 additions & 3 deletions spec/nmt.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,10 @@ As an example, the namespace proof for `nID = 0` for the NMT of Figure 1 (which

### Verification of NMT Absence Proof

An NMT absence proof is deemed valid if
1) The verification of Merkle inclusion proof of the returned leaf is valid.
2) It satisfies the proof completeness as explained in the [Verification of NMT Inclusion Proof](#verification-of-nmt-inclusion-proof).
An NMT absence proof is deemed valid if it meets the following:
1) The minimum namespace ID of the leaf in the proof is greater than the queried `nID`.
rootulp marked this conversation as resolved.
Show resolved Hide resolved
2) The verification of Merkle inclusion proof of the returned leaf is valid.
3) It satisfies the proof completeness as explained in the [Verification of NMT Inclusion Proof](#verification-of-nmt-inclusion-proof).
Note that hash of the leaf does not have to be verified against the underlying message.

### Verification of Empty NMT proof
Expand Down