Cryptographic verification is a process that uses mathematical algorithms to confirm the authenticity, integrity, and origin of data. It uses techniques such as hashing and digital signatures to verify information has not been tampered with or altered during transmission or storage.
It serves three purposes:
Example: Cryptocurrency transaction verification
Cryptographic verification is widely employed across sectors and industries:
Cryptographic verification guarantees blockchain transactions are authenticated, secure, and immutable. Here’s how:
Let's look at a practical example of digital signatures using ether.js
, a widely-used Ethereum library. When we create a digital signature, several things happen behind the scenes:
First, let’s sign a message:
import { ethers } from 'ethers';
async function createSignature() {
try {
// Create a wallet with a private key (NEVER use this private key in production!)
const privateKey = "your_priv_key";
const wallet = new ethers.Wallet(privateKey);
// The message we want to sign
const message = "Hi, from Cyfrin";
// Sign the message
const signature = await wallet.signMessage(message);
console.log("Message:", message);
console.log("Signer address:", wallet.address);
console.log("Signature:", signature);
} catch (error) {
console.error("Error:", error.message);
}
}
// Run the function
createSignature();
The library handles the cryptographic heavy lifting: it hashes our message using keccak256 and creates a signature using ECDSA (Elliptic Curve Digital Signature Algorithm), the standard signature mechanism in Ethereum.
This generates:Message: Hi, from Cyfrin
Signer address: 0xd520d565812F31345c32314e949a39FbB382551B
Signature: 0xa672589ad60e71fca65d10b018f94148603e3251abced9bc61bb855208637e064141f8662f56af0c0c159374aa6692f040292cd1efec8cc20b370473c7e6dc341c
To verify this signature, we can use this example:
import { ethers } from "ethers";
const message = "Hi, from Cyfrin";
const signature = "0xa672589ad60e71fca65d10b018f94148603e3251abced9bc61bb855208637e064141f8662f56af0c0c159374aa6692f040292cd1efec8cc20b370473c7e6dc341c";
const expectedSigner = "0xd520d565812F31345c32314e949a39FbB382551B";
try {
const recoveredAddress = ethers.verifyMessage(message, signature);
const isValid =
recoveredAddress.toLowerCase() === expectedSigner.toLowerCase();
if (isValid) {
console.log("✅ Signature is valid!");
console.log(`Message was signed by: ${recoveredAddress}`);
} else {
console.log("❌ Invalid signature!");
console.log(`Message was NOT signed by ${expectedSigner}`);
}
} catch (error) {
console.log("❌ Error verifying signature:", error.message);
}
When the message and signature match, the verification succeeds and it outputs:
✅ Signature is valid!
Message was signed by: 0xd520d565812F31345c32314e949a39FbB382551B
However, if even a single character in the message is changed (e.g., "Cyfrin" to "Cyfri"), the verification fails:
❌ Invalid signature!
Message was NOT signed by 0xd520d565812F31345c32314e949a39FbB382551B
The verification process recreates the message hash and uses the signature to recover the signer's address. Even if one character in the message changes, it produces a different hash and verification fails. The signature is uniquely tied to both the message and the signer's private key, which shows that modifying the message would invalidate the signature.