My understanding is that, for the even more special case where
b are not only of equal length but some power of two times a fixed block size, all hash tree systems (also called a Merkle tree system or a binary hash chain) meet your criteria.
E.g. satisfying following relation
h(a || b) = h(a) · h(b), where
h(x) is hash function itself,
x || y is concatenation and
x · y
is some hash-specific combination function making single hash given
two of them.
In particular, the hash specified by the Tree Hash EXchange format (THEX) spec
uses the hash-specific combination function
x · y == SHA1( 0x00 || x || y ).
whenever the underlying pieces of text
b are the same length and are both some power of two times a fixed block size.
d are exactly one block in size,
the tree hash T() used in THEX is defined something like
T(c) == SHA1( 0x01 || c ) # only for 'c' exactly 1 block long
T(d) == SHA1( 0x01 || d ) # only for 'd' exactly 1 block long
T( c || d ) == SHA1( 0x00 || T(c) || T(d) )
== SHA1( 0x00 || SHA1( 0x01 || c ) || SHA1( 0x01 || d ) )
Typically a block has a size of 1024 bytes; Dan Williams and Emin G¨un Sirer have written a paper on picking an optimal block size.
There are apparently two common ways to avoid the easy collisions described by
" What is the purpose of using different hash functions for the leaves and internals of a hash tree? ":
- some Merkle trees -- such as the THEX described above -- use one hash function for the leaves, and a different hash function for the internal nodes.
- Other Merkle trees -- such as the one used by BitTorrent -- keep track of both the file length and the root SHA1 hash value, and files are considered "the same" only if both match. This allows them to use the same unmodified SHA1 hash function for both the leaves and the internal nodes. (Some people think of this as a single "tree hash value" that includes two parts, the file length and the cryptographic hash value).
Merkle trees can handle files with size that is not a power of two --
How does a "Tiger Tree Hash" handle data whose size isn't a power of two? --
but if the first file isn't a power of two times the fixed block size, concatenating 2 files doesn't give the nice relationship you wanted between the hash of the two smaller files and the hash of the bigger combined file.