Lab: sign, verify, then tamper
Generate an Ed25519 keypair, sign a message, verify it with the public key alone, and watch one changed character destroy the signature.
The previous lesson claimed that a signature proves authorship and detects modification, and that verification needs no secret. Three minutes of openssl turns all three claims into things you have seen.
- 1
Make a scratch directory and generate an Ed25519 private key.
bashmkdir -p /tmp/siglab && cd /tmp/siglab openssl genpkey -algorithm ed25519 -out private.pem - 2
Derive the public key. This is the half you hand out, and it cannot be used to sign.
bashopenssl pkey -in private.pem -pubout -out public.pem cat public.pem - 3
Write a message worth being careful about, and sign it with the private key.
bashprintf 'transfer 10 ETH to 0xAlice' > msg.txt openssl pkeyutl -sign -inkey private.pem -rawin -in msg.txt -out msg.sig wc -c < msg.sig # 64 bytes, regardless of message length - 4
Verify using only the public key. Note what you did not need: the private key, a shared secret, or a call to anyone.
bashopenssl pkeyutl -verify -pubin -inkey public.pem -rawin -in msg.txt -sigfile msg.sig # -> Signature Verified Successfully - 5
Now tamper. Change the recipient and check the original signature against it.
bashprintf 'transfer 10 ETH to 0xMallory' > tampered.txt openssl pkeyutl -verify -pubin -inkey public.pem -rawin -in tampered.txt -sigfile msg.sig # -> Signature Verification Failure - 6
Confirm the message was never hidden. The signature protects it; it does not conceal it.
bashcat msg.txt # -> transfer 10 ETH to 0xAlice - 7
One more check, and it is the one that matters later. Generate a second, unrelated keypair and verify the original signature against it.
bashopenssl genpkey -algorithm ed25519 -out other.pem openssl pkey -in other.pem -pubout -out other.pub openssl pkeyutl -verify -pubin -inkey other.pub \ -rawin -in msg.txt -sigfile msg.sig - 8
It fails, which is correct and is not the interesting part. The interesting part is what that implies.
textSignature Verification Failure - 9
Clean up.
bashcd /tmp && rm -rf siglab
A signature carries no indication of which key produced it. Verification answers "was this signed by the key I am holding", and it can only answer that because you chose which key to check against. Choose the wrong one and a perfectly genuine signature is rejected; fail to pin down which one is correct and you will eventually accept a genuine signature from the wrong signer.
That is the whole problem the next lessons keep returning to. A JWT names its key in the kid header and its intended recipient in aud, and a verifier that checks the signature without checking those is asking the wrong question correctly.
The signature stayed 64 bytes whatever you signed, because signing operates on a fixed-length hash of the message rather than the message itself. That is also why the hash function's collision resistance is part of the security of the scheme and not a separate concern.
You verified with a public key and could not have signed with it. That asymmetry is what makes federated trust possible: a relying party validates 1Claw-issued tokens against published JWKS without holding any shared secret.
Now imagine signing msg.sig's contents without ever seeing msg.txt. That is blind signing, and it is why raw digest signing is gated behind a human-set flag in the Intents API.
Check your understanding
3 questionsThe signature was 64 bytes for a short message. What would it be for a 4GB file?
You verified the signature with public.pem. Could you have produced a signature with it?
After tampering, verification failed. What did that demonstrate?