How proof storage works — hashes, Merkle trees, the blockchain and signatures

This page explains what the proof package issued by TEL050JP proof storage (proof.json, certificate.pdf, certificate.sig.json, history.json and verify.html) shows, and on what mathematical grounds. It is a technical explanation. It says nothing about legal effect or about how the proof will be weighed as evidence; that is for a court or other proceeding to decide.

1. What the proof states, and what it does not

The proof package states three things.

  1. That the file with the stated SHA-256 corresponds to the stated communication record.
  2. That TEL050JP issued the proof.
  3. That the hash existed no later than the stated finalized block.

It does not state that the contents are true, who was speaking, that a recording was consented to or lawful, or who holds rights in the file. The mechanism detects tampering; it does not physically prevent a file from being changed. A changed file is detected because it no longer matches the proof.

2. The cryptographic hash function SHA-256

Everything starts with SHA-256, which computes a fixed 256-bit value H(x) (64 hexadecimal digits) from the bytes x of a file [1]. The proof relies on three properties expected of SHA-256 [2][3].

  • Preimage resistance: recovering the bytes from the hash alone takes on the order of 2256 attempts, which is out of reach.
  • Second-preimage resistance: producing a different file with the same hash as a given one is equally out of reach. A file that differs by a single bit has an entirely different hash.
  • Collision resistance: finding any two files with the same hash costs about 2128 attempts by the birthday bound, again out of reach.

So if a SHA-256 recorded at some point matches the SHA-256 recomputed from the file in hand, the file has not changed by a single bit since then. If they differ, something has. The package carries two such values: the hash of the original audio or fax file (artifact.sha256) and the hash of the canonicalised JSON communication record — parties, times and so on (communication_record_hash).

3. Canonicalisation and domain separation

Two JSON documents with the same content but different key order or whitespace have different bytes, and so different hashes. The communication record is therefore turned into canonical JSON before hashing: keys sorted, strings normalised to Unicode NFC, no extra whitespace. The same idea is standardised as the JSON Canonicalization Scheme [4]. Everyone computing the bytes gets the same bytes, which is what independent verification requires.

Every hash input also begins with a one-byte domain separator naming its purpose: 0x00 for a Merkle leaf, 0x01 for an inner node, 0x02 for the signature over the proof manifest (proof.json), 0x03 for the signature over the certificate PDF. Certificate Transparency [5] prefixes leaves and inner nodes differently for the same reason: it rules out, by construction, reading a hash or signature made for one purpose as if it were made for another. Variable-length fields are length-prefixed before concatenation so that boundaries are never ambiguous.

4. The Merkle tree and the audit path

TEL050JP gathers many proofs into one blockchain record using a Merkle tree [6][7]. Each proof's leaf Li is the SHA-256 of 0x00 followed by the tenant id, the communication id, the record hash, the file hash, the retention deadline, the extension sequence and the previous leaf (all zeros for a first proof). Two adjacent nodes are combined into a parent as H(0x01 ‖ left ‖ right), and this repeats up to a single root. On a level with an odd count the last node is carried up unchanged rather than duplicated, because duplication would let two different sets of leaves share one root.

Each proof.json contains the sibling nodes on the way from its own leaf to the root — the audit path. A verifier recomputes the leaf, folds the hashes up the path and checks that the result equals the root recorded on the blockchain. The path has ⌈log2 n⌉ entries for n proofs, ten for a batch of a thousand. Nothing about the other proofs in the batch is present in the path, so nothing about other customers is disclosed.

5. The blockchain record and "existed by"

Showing that a hash existed at a given time, without relying on the issuer's own word, goes back to the work of Haber and Stornetta on digital time-stamping [8][9]. The idea is to publish the hash in a record the issuer cannot reach back into. Blockchains since Bitcoin [10] are public records kept identically by many independent nodes, with blocks chained in time order, and suit this purpose.

TEL050JP sends a transfer transaction on the Symbol blockchain [11] from a signing account reserved for this use, with the root in the message field in the fixed form tel050jp:proof:v1 kind=communication root=sha256:<root> batch=<batch id>. The transaction is recorded with a block height, and blocks up to the height reached by Symbol's voting-node finalization procedure are never rolled back. A proof package is issued only after the recorded height is confirmed to be at or below the finalized height. The block timestamp is a value agreed by the many nodes of the Symbol network; TEL050JP cannot move it back afterwards. The wording "existed no later than" follows from this structure.

Unlike a timestamp from a public time-stamping authority (RFC 3161 [12]), no single authority has to be trusted; in exchange, the scheme depends on the public ledger persisting. Section 9 returns to this.

6. The issuer signature, Ed25519

That TEL050JP issued the proof is shown by a digital signature. The body of proof.json without its signature field is canonicalised, prefixed with 0x02 and the schema name, and signed with TEL050JP's Ed25519 private key. Ed25519 [13][14] is a signature scheme on the elliptic curve Curve25519 with 32-byte public keys, 64-byte signatures and about 128 bits of security; forging a signature requires the private key. The matching public key is printed in the "Issuer signing key" row of certificate.pdf and embedded in the allowlist inside verify.html, so a third party can check that proof.json was signed under that key without asking TEL050JP.

The Symbol account key used to write to the blockchain and the key used to sign proof.json are different keys. The former writes to the public ledger; the latter says "this proof is TEL050JP's". A problem with one does not spread to the other.

Signer ledger (ledger.json)

The package includes a snapshot of the public signing keys, their identifiers, validity intervals and status: active, retired or compromised. It contains no private keys. Select ledger.json in verify.html to check its canonical format and match it against the verifier's own snapshot or the current ledger confirmed by the hosted lookup. An uploaded ledger cannot make its own keys trusted.

The verifier checks proof and history signing keys against the ledger, rejects compromised keys and checks the declared issuance time against the validity interval. Retired keys may verify historical signatures within their interval. The declared issuance time is not an independently proven signing time. PDF signatures contain no signing timestamp, so their key status can be checked but their validity interval remains unchecked.

The hosted verifier requests only public ledger metadata from TEL050JP; selected files are never uploaded. It compares the current ledger hash with the latest finalized ledger announcement through the server's configured Symbol node and reports the transaction hash, height and check time. This depends on that node, not on independent blockchain consensus verification in the browser. Missing anchors, node failures and incomplete bounded searches remain unchecked. Offline verification always leaves current revocation and the public ledger announcement unchecked. Existing ZIPs do not update themselves. Testnet records may disappear when the test network resets.

7. The certificate PDF and its detached signature

certificate.pdf is the document for people to read; the authoritative data for verification is proof.json. The PDF is generated from proof.json at each download, so the signature on proof.json does not cover it. Instead the SHA-256 of the PDF bytes is placed after 0x03 and the schema name, signed with the same Ed25519 key, and shipped as certificate.sig.json. Given the PDF and the sig, verify.html shows a mismatch if the PDF has been edited by even one byte. The look of the PDF — the rules, the colours — is decoration and proves nothing by itself. The signature is what proves.

8. Independent verification (verify.html)

The package includes a verifier, verify.html, as a single HTML file. It loads no external scripts and contacts no server; using only the browser's built-in WebCrypto it checks:

  1. the schema of proof.json and the issuer signature (Ed25519, against the allowlist);
  2. that the SHA-256 of the file in hand equals artifact.sha256;
  3. that the SHA-256 of communication.json equals communication_record_hash;
  4. that the leaf recomputed from the values in proof.json alone, folded up the audit path, equals the root;
  5. that the message written to the blockchain is the fixed form determined by that root and batch id;
  6. that the recording Symbol account and network match the allowlist, and the block height is at or below the finalized height;
  7. if history.json is present, that each version is chained to the previous leaf;
  8. if certificate.pdf and certificate.sig.json are present, that the PDF is the one that was signed.

The blockchain record itself can be looked up by anyone on a public Symbol node or block explorer using the transaction hash. verify.html establishes that proof.json is internally consistent and that the stated root must have been recorded as the stated message; whether that record is on the ledger is confirmed against the public ledger. No TEL050JP server is needed at any step.

The hosted verifier checks files in your browser and also requests current public ledger metadata and anchor status. Selected files are never uploaded. If the packaged verifier is suspect, obtain a fresh copy from a trusted source. Offline copies cannot check subsequent revocations.

9. What is detected and what is not

SituationOutcome
Part of the file is changed afterwardsSHA-256 no longer matches: detected
The communication record (numbers, times) is changed afterwardsThe record hash no longer matches: detected
The certificate PDF is editedDoes not match certificate.sig.json: detected
Values in proof.json are changedThe issuer signature fails; the leaf and root no longer recompute: detected
The proof is made to look older than it isThe block timestamp is a consensus value on a public ledger that the issuer cannot change
Someone other than TEL050JP produces a proof in TEL050JP's nameWithout the signing key the signature fails; the Symbol account is outside the allowlist
The file was edited before it was hashedNot detected. The proof shows immutability from the moment of recording onwards
The content is untrue, or the speaker is someone elseOut of scope. The proof does not address truth of content or identity of persons

If a key is compromised: the key is removed from current trust, including for past signatures. Old ZIPs do not update automatically. Use the hosted verifier to check current ledger status. A blockchain anchor alone does not restore trust in a compromised signing key.

If TEL050JP ceases operation: verification needs only proof.json, the file in hand, verify.html and the public ledger, and does not depend on TEL050JP's servers. If the Symbol network stops: verification remains possible as long as copies of the ledger or block-explorer archives exist. For long-term preservation, keep the transaction record (block height, timestamp, message) alongside the proof package.

10. Terms

SHA-256
A hash function computing a 256-bit value from input of any length. NIST standard [1].
Merkle tree
A binary tree of hashes in which one root stands for the whole set [6].
Audit path
The sibling nodes needed to recompute the root from one leaf.
Finalization
In Symbol, the agreement of voting nodes that blocks up to a given height are settled and will not be rolled back [11].
Ed25519
A digital signature scheme on the elliptic curve Curve25519 [13][14].
Canonical JSON
A JSON representation with fixed key order, whitespace and string normalisation, so that equal content always gives equal bytes [4].
Domain separation
Prefixing hash and signature inputs with a byte naming their purpose, so purposes cannot be confused [5].

11. References

  1. National Institute of Standards and Technology. Secure Hash Standard (SHS). FIPS PUB 180-4, 2015. doi:10.6028/NIST.FIPS.180-4
  2. P. Rogaway and T. Shrimpton. "Cryptographic Hash-Function Basics: Definitions, Implications, and Separations for Preimage Resistance, Second-Preimage Resistance, and Collision Resistance." Fast Software Encryption (FSE 2004), LNCS 3017, Springer, 2004, pp. 371–388.
  3. J. Katz and Y. Lindell. Introduction to Modern Cryptography, 3rd ed. CRC Press, 2020.
  4. A. Rundgren, B. Jordan, and S. Erdtman. JSON Canonicalization Scheme (JCS). RFC 8785, IETF, 2020.
  5. B. Laurie, A. Langley, and E. Kasper. Certificate Transparency. RFC 6962, IETF, 2013.
  6. R. C. Merkle. "A Digital Signature Based on a Conventional Encryption Function." Advances in Cryptology — CRYPTO '87, LNCS 293, Springer, 1988, pp. 369–378.
  7. R. C. Merkle. "Protocols for Public Key Cryptosystems." IEEE Symposium on Security and Privacy, 1980, pp. 122–134.
  8. S. Haber and W. S. Stornetta. "How to Time-Stamp a Digital Document." Journal of Cryptology 3(2), 1991, pp. 99–111.
  9. D. Bayer, S. Haber, and W. S. Stornetta. "Improving the Efficiency and Reliability of Digital Time-Stamping." Sequences II: Methods in Communication, Security, and Computer Science, Springer, 1993, pp. 329–334.
  10. S. Nakamoto. Bitcoin: A Peer-to-Peer Electronic Cash System. 2008.
  11. NEM Group. Symbol Technical Reference. 2021. https://docs.symbol.dev/
  12. C. Adams, P. Cain, D. Pinkas, and R. Zuccherato. Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP). RFC 3161, IETF, 2001.
  13. D. J. Bernstein, N. Duif, T. Lange, P. Schwabe, and B.-Y. Yang. "High-speed high-security signatures." Journal of Cryptographic Engineering 2(2), 2012, pp. 77–89.
  14. S. Josefsson and I. Liusvaara. Edwards-Curve Digital Signature Algorithm (EdDSA). RFC 8032, IETF, 2017.

This page follows the proof package specification tel050jp-communication-proof-v1. Specifications are published per version; if the specification is revised, proofs issued under an older version stay verifiable under that version's rules. The version of a proof is identified by the protocol field of proof.json (the protocol version) and its schema field (the package format version).