cyphrex-signer-prod-01
Ed25519 signing key ยท Active since 2026-05-06 ยท Public record
This page is HTML. Verifiers must fetch the key from the PEM endpoint, not from this URL.
https://cyphrex.io/api/keys/cyphrex-signer-prod-01
Same bytes from the API: https://cyphrexapi-production.up.railway.app/v1/keys/cyphrex-signer-prod-01
Loading key...
Fetch this key
curl https://cyphrex.io/api/keys/cyphrex-signer-prod-01
Build canonical payload + verify (Node.js, RFC 8785 JCS)
Packages are tagged canonicalization: "rfc8785-jcs". Canonicalize with an RFC 8785 library (npm canonicalize), not sorted-key JSON.stringify. Strip only sha256, signature, algorithm, and publicKeyUrl. Keep canonicalization and the full timestamp object in the signed core.
// =============================================================================
// Cyphrex signed report โ RFC 8785 JCS + verify (schemaVersion 1.0.0)
// =============================================================================
// Node.js 18+. npm install canonicalize@2
// report.canonicalization === "rfc8785-jcs"
//
// You must define:
// const report = ... // full object from POST /v1/events/export-signed
// const publicKeyPem = `...` // PEM from GET https://cyphrex.io/api/keys/<keyId>
// =============================================================================
const canonicalize = require('canonicalize');
const { createHash, verify, createPublicKey } = require('crypto');
const STRIP = new Set(["sha256", "signature", "algorithm", "publicKeyUrl"]);
if (typeof report === 'undefined') throw new Error('Define `report`.');
if (typeof publicKeyPem === 'undefined') throw new Error('Define `publicKeyPem`.');
const core = Object.fromEntries(Object.entries(report).filter(([k]) => !STRIP.has(k)));
if (core.canonicalization !== "rfc8785-jcs") {
throw new Error(`Unsupported canonicalization: ${core.canonicalization}`);
}
const canonical = canonicalize(core);
const publicKey = createPublicKey(publicKeyPem);
const isValid = verify(
null,
Buffer.from(canonical, 'utf8'),
publicKey,
Buffer.from(report.signature, 'base64')
);
if (!isValid) throw new Error("Invalid signature");
const computedHash = createHash('sha256').update(canonical, 'utf8').digest('hex');
if (computedHash !== report.sha256) throw new Error('Hash mismatch');
console.log('Valid signature');Python (rfc8785 + cryptography only)
# pip install rfc8785 cryptography
# report.canonicalization == "rfc8785-jcs"
import base64, hashlib, json
import rfc8785
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.serialization import load_pem_public_key
STRIP = ("sha256", "signature", "algorithm", "publicKeyUrl")
report = json.loads(open("cyphrex-report.json", encoding="utf-8").read())
pem = open("cyphrex-public.pem", "rb").read()
if report.get("canonicalization") != "rfc8785-jcs":
raise SystemExit("Unsupported canonicalization: " + str(report.get("canonicalization")))
core = {k: v for k, v in report.items() if k not in STRIP}
canonical = rfc8785.dumps(core)
if isinstance(canonical, str):
canonical = canonical.encode("utf-8")
# Check order: Ed25519 first (always), then SHA-256 of the same JCS bytes.
key = load_pem_public_key(pem)
try:
key.verify(base64.b64decode(report["signature"]), canonical)
except InvalidSignature:
raise SystemExit("Invalid signature")
digest = hashlib.sha256(canonical).hexdigest()
if digest != report["sha256"]:
raise SystemExit("Hash mismatch")
print("Valid signature")Inclusion proof (membership in the on-chain root)
Signature check alone does not walk the Merkle path. Download verify_inclusion_independent.py (rfc8785 + cryptography only). It picks the first anchored: true event by default.
curl -fsS -o verify_inclusion_independent.py https://cyphrex.io/tools/verify_inclusion_independent.py curl -fsS -o cyphrex-public.pem https://cyphrex.io/api/keys/cyphrex-signer-prod-01 python3 verify_inclusion_independent.py report.json cyphrex-public.pem
Verify only (Node.js)
// =============================================================================
// Cyphrex signed report โ verify only (schemaVersion 1.0.0, rfc8785-jcs)
// =============================================================================
// Node.js 18+. Built-in `crypto` only.
//
// You must define:
// canonical โ UTF-8 string from RFC 8785 JCS of the signed core
// (NOT JSON.stringify / sorted-key stringify)
// signature โ base64 string from report.signature
// publicKeyPem โ PEM from this registry page
// =============================================================================
const { verify, createPublicKey } = require('crypto');
if (typeof canonical === 'undefined') throw new Error('Define `canonical` (see comments at top of this snippet).');
if (typeof signature === 'undefined') throw new Error('Define `signature` (see comments at top of this snippet).');
if (typeof publicKeyPem === 'undefined') throw new Error('Define `publicKeyPem` (see comments at top of this snippet).');
const publicKey = createPublicKey(publicKeyPem);
const isValid = verify(
null, // null algorithm for Ed25519
Buffer.from(canonical, 'utf8'),
publicKey,
Buffer.from(signature, 'base64')
);
console.log(isValid ? 'Valid signature' : 'Invalid signature');