Hashes, Signatures and Merkle Proofs: What Does Each Actually Prove?
A customer sends an approved order. A colleague sends its hash. A remote service sends a Merkle proof. All three may be useful, but they do not answer the same question. Confusing them can leave an application checking the right mathematics against the wrong trust assumption.
We will use one small C# example to compare the claims. Sphere 10 Framework supplies the hashing primitives, and its CryptoEx package supplies the signing implementation. The sample changes the order text and shows which checks reject it, rather than treating the presence of a cryptographic value as sufficient proof.
Contents
Install the Packages
dotnet new console --framework net10.0
dotnet add package Sphere10.Framework --version 3.1.3
dotnet add package Sphere10.Framework.CryptoEx --version 3.1.3
The complete downloadable project pins those versions. Its two-leaf tree makes the membership calculation visible. For a general dataset, the Framework proof tutorial demonstrates the collection-based API.
A Hash Identifies the Bytes You Checked
var hash = Hashers.Hash(CHF.SHA2_256, message);
var changed = Hashers.Hash(CHF.SHA2_256, altered);
Console.WriteLine(!hash.SequenceEqual(changed));
The result is True for the two messages in the sample. A hash is useful when a trusted source has already supplied the expected digest. If an attacker can replace both the message and the digest, recomputing the digest alone does not identify the sender. A digest downloaded from the same unauthenticated location as its file needs an additional trust mechanism.
A Signature Is Checked Against a Public Key
var signer = new ECDSA(ECDSAKeyType.SECP256K1);
var privateKey = signer.GeneratePrivateKey();
var publicKey = signer.DerivePublicKey(privateKey);
var signature = signer.Sign(privateKey, message);
Console.WriteLine(signer.Verify(signature, message, publicKey));
Console.WriteLine(signer.Verify(signature, altered, publicKey));
The original verifies and the altered message does not. This establishes a relationship between the signed bytes and the corresponding key, under the scheme's assumptions. The application still needs a trustworthy association between that public key and the person, device or authority it intends to accept. The sample creates an ephemeral key to demonstrate signing. A production application also needs a key-storage design.
A Merkle Proof Establishes Membership
In the sample, two leaf hashes produce one root. To check the first record, the verifier combines that record's leaf with the supplied sibling and compares the result with the expected root. Leaves and parent nodes have different prefixes so their roles are unambiguous in this illustrative tree.
var sibling = Leaf(Encoding.UTF8.GetBytes("Order 1043"));
var trustedRoot = Parent(Leaf(message), sibling);
var valid = trustedRoot.SequenceEqual(Parent(Leaf(message), sibling));
The downloadable project contains Leaf and Parent. Replacing message with altered makes this comparison False. The proof does not independently say who endorsed the dataset. A signed or otherwise trusted root can supply that additional boundary.
Read the Expected Result
Changed bytes change digest: True
Original signature verifies: True
Altered message verifies: False
Original two-leaf proof verifies: True
Altered two-leaf proof verifies: False
A dataset root can also change over time. Keep the root's version or context with the proof, and define what freshness means for the application. None of the three checks automatically supplies authorisation, replay prevention or business approval.
Continue with record inclusion proofs and Merkleized ObjectSpaces . The CryptoEx guide explains the Framework's supported signature abstractions. The useful engineering result is a precise verification claim, not simply more cryptographic terminology in the system.
Continue the Series
How to Prove One Record Belongs to a Dataset
How to Add Cryptographic Integrity to an Object Store
For the broader Sphere 10 development toolkit, see the Sphere10 Framework overview .