Support

We're here to help you get the most out of Truestamp. Find answers to common questions or get in touch with our support team.

Community & Updates

Share feedback, follow what we're building next, and see what just shipped. Each opens right here in a popup, so no separate account is needed.

Knowledge Base

Browse the concepts behind Truestamp. Pick a domain to explore, follow the links between concepts, or search across everything.

Domain-Separated Hashing (SHA-256 Byte Prefixes)

How Truestamp separates SHA-256 hash contexts with single-byte domain prefixes (RFC 6962 style), keeping leaf hashes, node hashes, and each kind of hashed object in distinct hash spaces to prevent ambiguity and second-preimage confusion.

Overview

A hash function like SHA-256 takes any input and produces a fixed 32-byte fingerprint. Truestamp uses SHA-256 everywhere, for many different kinds of data: user submissions, blocks, entropy observations, the nodes of a Merkle tree, and more. If every one of those used plain SHA-256, a fingerprint computed for one purpose could be reinterpreted as a fingerprint for another purpose. Domain separation fixes this by putting a single distinguishing byte (a “domain prefix”) at the front of the input before hashing. The same bytes hashed under two different prefixes produce two unrelated fingerprints, so each context gets its own private hash space that cannot be confused with any other. This is the approach standardized by RFC 6962 (Certificate Transparency) and used by Bitcoin, Ethereum, and other large systems.

Why hashing needs domain separation

A hash on its own answers “here is a 32-byte fingerprint,” but it does not say what the fingerprint is a fingerprint of. That missing context is where attacks live.

Consider two completely different things that both get hashed:

  • a piece of user-submitted data, and
  • an internal record the system generated.

If both are hashed with plain SHA-256(data), then a fingerprint the system produced for one is byte-for-byte a valid fingerprint for the other. An attacker who can get a value accepted in the “cheap” context can try to pass it off in the “trusted” context, because nothing in the fingerprint distinguishes the two. This is a form of second-preimage confusion: the attacker does not need to break SHA-256 itself, they just need two contexts to accept the same fingerprint.

Domain separation removes that shared surface. Each context prepends its own reserved byte, so:

  • SHA-256(0x11 || data) (one context), and
  • SHA-256(0x21 || data) (a different context)

are different fingerprints even when data is identical. A value that is legitimate in the first context is meaningless in the second, because recomputing it there would require the second context’s prefix, which it does not have. The contexts are cryptographically walled off from one another.

How a byte prefix works

A domain prefix is exactly one byte placed immediately before the data, and then the whole thing is hashed as a unit. In Truestamp the core operation is simply SHA-256(prefix_byte || data), where || means “followed by.”

For example, user data (“claims”) uses the reserved prefix 0x11:

claims_hash = SHA-256(0x11 || data)

You can reproduce this in any language. In JavaScript:

const PREFIX_ITEMS_CLAIMS = 0x11;
const data = new TextEncoder().encode(jsonString);
const preimage = new Uint8Array([PREFIX_ITEMS_CLAIMS, ...data]);
const hash = await crypto.subtle.digest("SHA-256", preimage);

In Python:

import hashlib
PREFIX_ITEMS_CLAIMS = b"\x11"
digest = hashlib.sha256(PREFIX_ITEMS_CLAIMS + data).hexdigest()

Both produce the same 64-character hex fingerprint that Truestamp stores. Hashing the string "truestamp" under prefix 0x11 always gives 9d9443e5133052d9fc837e150d32e3094fc979f097922034a984ddfbe5247aca, and hashing that same string under a different prefix gives a completely unrelated value. That is the whole mechanism: one byte, prepended, changes which hash space the fingerprint belongs to.

Why a single byte is enough

A prefix only needs to be unique per context, not secret or large. A single byte gives 256 possible domains, which is far more than Truestamp needs, and it costs just one byte of overhead per hash. An alternative design uses a human-readable string tag (for example "items-claims:" in front of the data), but that adds tens of bytes of overhead per hash and requires more care to implement identically across languages. The single reserved byte is the minimal, unambiguous way to say “this fingerprint belongs to this domain.”

Separating leaf hashes from node hashes

The most important use of domain separation is inside a Merkle tree, the structure that lets Truestamp prove one item was included in a batch. A Merkle tree has two kinds of hashes:

  • leaf hashes, computed from the data being included, and
  • internal node hashes, computed by combining two child hashes.

RFC 6962 assigns these two distinct prefixes, and Truestamp follows it exactly:

  • a leaf is hashed as SHA-256(0x00 || leaf_data), and
  • an internal node is hashed as SHA-256(0x01 || left_child || right_child).

Without those prefixes, an attacker could take an internal node (which is a combination of two hashes) and present it to a verifier as if it were a leaf, or take a leaf and claim it is an internal node. Because leaves and nodes would live in the same hash space, a single 32-byte value could be valid in both roles, opening a second-preimage substitution attack against the proof. The 0x00 / 0x01 split makes a leaf hash and a node hash structurally different pre-images, so a value that is a legitimate leaf can never be reinterpreted as a legitimate node, and vice versa. Truestamp adds a second, independent guardrail here: every hash fed into the tree must be exactly 32 bytes, so an internal node (which is built from 64 bytes of input) can never masquerade as a leaf input on length alone.

Separating different kinds of hashed objects

Beyond the tree’s own leaf and node prefixes, Truestamp reserves a distinct prefix for every different kind of object it hashes. Each domain gets its own reserved value so that, for instance, a fingerprint of user claims can never be confused with a fingerprint of an entropy observation or of a block, even if the underlying bytes happened to match. The prefixes are grouped into ranges by domain (for example, the 0x1X range for user-item hashes, the 0x2X range for entropy, the 0x3X range for blockchain blocks), and the values are defined once in a single canonical registry and reused everywhere, so the same prefix always means the same thing across the whole system and across independent re-implementations. Within each range the last digit distinguishes sub-contexts, so a single reserved byte identifies both the domain and the exact object it covers. One of those domains covers the compact payload that Truestamp signs, so the fingerprint an Ed25519 signature is made over lives in its own hash space, separate from every other kind of hashed object.

What domain separation does and does not protect

Domain separation is a targeted defense. It is worth being precise about its scope.

It provides:

  • Context isolation. A fingerprint computed in one domain cannot be reused as a valid fingerprint in another, because the reserved prefix byte differs. This blocks cross-context and second-preimage substitution attacks without relying on the strength of the surrounding protocol.
  • Structural distinctness in the tree. Leaf hashes and internal-node hashes occupy separate hash spaces, so proof paths cannot be forged by swapping one role for the other.

It does not, by itself, provide:

  • Collision resistance or preimage resistance. Those come from SHA-256 itself, not from the prefix. Domain separation ensures collisions and preimages found in one domain stay confined to that domain.
  • Protection against reordering multiple fields. When Truestamp hashes several fields into one composite fingerprint (as in the item’s composite fingerprint), the prefix alone is not enough to keep the field boundaries unambiguous; a length-prefixed layout handles that. Domain separation and unambiguous field encoding are complementary defenses, not substitutes for each other.

A note on why length prefixes are not needed for the simple single-value case: SHA-256 is not vulnerable to length-extension attacks, and a single value has no field boundaries to confuse, so SHA-256(prefix || value) is safe on its own. Length prefixing is added only where multiple values are combined into one hash.

Citations

  1. RFC 6962: Certificate Transparency. Defines the 0x00 leaf and 0x01 internal-node domain prefixes for Merkle tree hashing that Truestamp’s leaf/node separation follows.

Get Help

API Documentation

Comprehensive guides for the REST and GraphQL APIs, including interactive documentation and code examples.

View API Docs

FAQ

Quick answers to the most commonly asked questions about timestamping and verification.

Browse FAQ

Email Support

Send us a message and our team will respond within 24 hours.

[email protected]

Security Issues

Report security vulnerabilities through our responsible disclosure program.

[email protected]