We hash a lot of different types of things, usually to commit to their value. We hash Scalars, Curve Points, IDs, and plain old byte slices. In theory, you could have an issue because you can't distinguish between say, a Curve Point, and two field elements. To resolve this, you can make sure that each type of thing you hash is surrounded by a different context. For example, instead of hashing a Curve point with bytes <bytes> you'd instead hash the byte string:
(Curve Point<bytes>)
This distinguishes these bytes from a generic byte slice, or from two field elements. The opening and closing parentheses also avoid issues where the bytes of one object contain the domain string of another object.
The tricky thing is making sure that this scheme is globally enforced.
To create an enforcement bottleneck, this patch only allows you to write into a Hash through a single method: Hash.WriteAny. Since everything goes through this method, we can make sure that domain separation happens.
This patch creates a new interface, analogous to io.WriterTo, that also contains a domain string. By using this string, our generic hash function can separate different types implementing this interface. For concrete types, it falls back on a straightforward domain string like []byte.
Finally, many one-off byte slices are wrapped with an appropriate domain string, to differentiate them from each other. For example, the bytes of the name of a curve are differentiated from the bytes of that curve's order.
Fixes #9.
We hash a lot of different types of things, usually to commit to their value. We hash Scalars, Curve Points, IDs, and plain old byte slices. In theory, you could have an issue because you can't distinguish between say, a Curve Point, and two field elements. To resolve this, you can make sure that each type of thing you hash is surrounded by a different context. For example, instead of hashing a Curve point with bytes
<bytes>
you'd instead hash the byte string:This distinguishes these bytes from a generic byte slice, or from two field elements. The opening and closing parentheses also avoid issues where the bytes of one object contain the domain string of another object.
The tricky thing is making sure that this scheme is globally enforced.
To create an enforcement bottleneck, this patch only allows you to write into a Hash through a single method:
Hash.WriteAny
. Since everything goes through this method, we can make sure that domain separation happens.This patch creates a new interface, analogous to
io.WriterTo
, that also contains a domain string. By using this string, our generic hash function can separate different types implementing this interface. For concrete types, it falls back on a straightforward domain string like[]byte
.Finally, many one-off byte slices are wrapped with an appropriate domain string, to differentiate them from each other. For example, the bytes of the name of a curve are differentiated from the bytes of that curve's order.