Verify a CDS event
This tutorial walks through verifying a CDS event you received from somewhere else — a file, an HTTP response, an MCP tool call. Verification is a local operation; it requires only the issuer’s public key. No network call is needed once the public key is in hand.
Prerequisites
Section titled “Prerequisites”- The event you want to verify (a JSON file or in-memory object)
- The issuer’s public key
- The CDS SDK installed (
pip install signeddata-cdsornpm install @signeddata/cds-sdk)
-
Obtain the issuer’s public key
The issuer publishes their public key at a well-known URL:
https://{issuer}/.well-known/cds-public-key.pemFor
signed-data.org:Terminal window curl -o signed-data-org.pub.pem \https://signed-data.org/.well-known/cds-public-key.pemSave the file somewhere your code can read it.
-
Load the event
import jsonwith open("event.json") as f:event_data = json.load(f)import { readFileSync } from "node:fs";const eventData = JSON.parse(readFileSync("event.json", "utf-8")); -
Verify the signature
from cds import CDSEvent, CDSVerifierevent = CDSEvent.from_jsonld(event_data)verifier = CDSVerifier("signed-data-org.pub.pem")try:verifier.verify(event)print("Valid — data is authentic and unmodified")except Exception as e:print(f"Invalid — {e}")import { CDSEvent, CDSVerifier } from "@signeddata/cds-sdk";const event = CDSEvent.fromJSON(eventData);const verifier = new CDSVerifier("signed-data-org.pub.pem");try {verifier.verify(event);console.log("Valid — data is authentic and unmodified");} catch (e) {console.log(`Invalid — ${(e as Error).message}`);} -
Try tampering with the event
Modify any field in the payload and verify again — the signature will be rejected immediately:
event.payload["temperature"]["current"] = 99.0 # tamper!verifier.verify(event) # raises: Hash mismatchAny change to any field — including
payload,context.summary, orsource.fingerprint— invalidates the signature. There is no way to selectively re-sign part of an event.
What verification proves
Section titled “What verification proves”A successful verification proves three things:
- Authenticity — the event was signed by the holder of the private key
matching this public key. If the issuer is
https://signed-data.org, the event was issued by signed-data.org. - Integrity — the canonical bytes have not changed since signing. Every field in the event is exactly as the issuer produced it.
- Source assertion — the issuer attests that the
payloadwas derived from thesource.@idupstream API atoccurred_at, and that the raw bytes matched the SHA-256 insource.fingerprint.
It does not prove that the upstream source was correct. The issuer signs what it received — not the underlying ground truth.
Cross-version verification
Section titled “Cross-version verification”v0.1.0 and v0.2.0 events use different canonical byte rules. A v0.1.0
verifier cannot verify a v0.2.0 event, and vice versa. Always check
spec_version first if you handle a mix of legacy and current events. See
Migration v0.1 → v0.2 for details.