Skip to content

BLAKE3 only supports fixed 32-byte output — should support variable length up to 128 bytes #49

Description

@sumanjeet0012

Blake3Hash always produces a 32-byte digest. Go's BLAKE3 registration supports variable-length output (32, 64, 128 bytes, up to max 128). Calling sum(data, "blake3", length=64) raises TruncationError because the digest is only 32 bytes, even though BLAKE3 natively supports extended output.

Problem

In multihash/funcs.py:

class Blake3Hash:
    digest_size: int = 32  # Always 32 bytes

    def digest(self) -> bytes:
        return self._hasher.digest()  # Always returns 32 bytes

The blake3 Python library supports variable-length output:

import blake3
h = blake3.blake3()
h.update(b"foo")
h.digest(64)   # Returns 64 bytes ✅
h.digest(128)  # Returns 128 bytes ✅

But the wrapper doesn't use this capability. So:

from multihash import sum, Func

# This raises TruncationError:
mh = sum(b"foo", Func.blake3, length=64)
# TruncationError: truncation length 64 exceeds digest size 32

Go's implementation uses RegisterVariableSize:

multihash.RegisterVariableSize(multihash.BLAKE3, func(size int) (hash.Hash, bool) {
    if size == -1 { size = 32 }
    else if size > 128 || size <= 0 { return nil, false }
    h := blake3.New(size, nil)
    return h, true
})

Go test vectors include BLAKE3 at 32, 64, and 128 bytes.

Proposed Solution

Modify Blake3Hash to accept a length parameter:

class Blake3Hash:
    MAX_SIZE = 128

    def __init__(self, length: int = 32):
        self._hasher = blake3.blake3()
        self._length = length
        self.name = "blake3"
        self.digest_size = length
        self.block_size = 64

    def digest(self) -> bytes:
        return self._hasher.digest(self._length)

    def copy(self):
        c = Blake3Hash(self._length)
        c._hasher = self._hasher.copy()
        return c

Update FuncReg.hash_from_func() to pass length for BLAKE3 (similar to SHAKE):

@classmethod
def hash_from_func(cls, func, length=None):
    new = cls._func_hash[func].new
    if new is None:
        # Handle SHAKE and BLAKE3 with variable length
        if func == Func.blake3:
            blake3_len = length if length and length > 0 else 32
            if blake3_len > Blake3Hash.MAX_SIZE:
                return None
            return Blake3Hash(blake3_len)
        # ... existing SHAKE handling ...
    return new()

Add tests matching Go's test vectors for BLAKE3 at 32, 64, and 128 bytes.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions