multiformats / multiformats/py-multihash
Identity hash doesn't enforce `length == len(data)` constraint
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 17
- Forks
- 17
- Avg merge
- 1h 5m
- Merged PRs (30d)
- 3
Description
Go's Sum() explicitly rejects identity hashes where the requested truncation length doesn't match the data length. Python's _do_digest() doesn't have this check, allowing invalid identity multihashes to be created.
Problem
In Go's sum.go:
func encodeHash(hasher hash.Hash, code uint64, length int) (Multihash, error) {
sum := hasher.Sum(nil)
if length >= 0 {
if code == IDENTITY {
if length != len(sum) {
return nil, fmt.Errorf(
"the length of the identity hash (%d) must be equal to the length of the data (%d)",
length, len(sum))
}
}
sum = sum[:length]
}
return Encode(sum, code)
}
Python's _do_digest() has no equivalent check:
def _do_digest(data, func, length=None):
# ... hash computation ...
# Handle truncation
if not is_shake and length is not None and length != -1:
if length > len(digest_bytes):
raise TruncationError(...)
digest_bytes = digest_bytes[:length] # ← No identity check!
return digest_bytes
This allows creating invalid identity multihashes:
from multihash import sum, Func
# Go would reject this, Python allows it:
mh = sum(b"hello world", Func.identity, length=5)
# Creates a multihash with truncated identity hash — invalid per spec
Proposed Solution
Add an identity hash check in _do_digest():
def _do_digest(data, func, length=None):
# ... existing code ...
if not is_shake and length is not None and length != -1:
if length < 0:
raise TruncationError(...)
if length == 0:
raise TruncationError("truncation length cannot be zero")
# Identity hash: length must equal data length
if func == Func.identity and length != len(digest_bytes):
raise TruncationError(
f"the length of the identity hash ({length}) must be equal "
f"to the length of the data ({len(digest_bytes)})"
)
if length > len(digest_bytes):
raise TruncationError(...)
digest_bytes = digest_bytes[:length]
return digest_bytes
Add the same check in sum_stream().
Add tests:
def test_identity_hash_length_must_match():
# Valid: length matches data
mh = sum(b"hello", Func.identity, length=5)
assert mh.digest == b"hello"
# Invalid: length doesn't match
with pytest.raises(TruncationError, match="identity hash"):
sum(b"hello", Func.identity, length=3)
Related
- Go implementation: go-multihash
sum.goencodeHash() - Go test:
TestSmallerLengthHashIDinsum_test.go
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating _do_digest() and sum_stream() in the Python implementation and review their existing truncation handling. Add coverage for matching and mismatched identity lengths, including the expected TruncationError, and run the relevant test suite to confirm valid identity hashes remain unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- cryptography
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100