Signing and Verification

PhronEdge signs every policy and every credential with ECDSA P-256. The signature is the trust. Any party with the public key can verify any signed artifact. No trust in PhronEdge required. The math proves authenticity.

This document covers how signing works, how keys are managed, how to rotate them, and how to verify a policy or credential independently.

Why cryptographic signing

Without signatures, a governance claim is just an assertion. "Agent A is allowed to call tool B in jurisdiction C" is trust-me words. With a signature, it's a provable statement: these rules were approved by the holder of this private key, at this time, and have not been modified since.

For a regulated enterprise, this produces three enterprise-critical outcomes:

  1. 1.Non-repudiation. Your organization cannot deny having signed the policy.
  2. 2.Tamper evidence. Any modification to a signed policy, credential, or audit event is mathematically detectable.
  3. 3.Independent verifiability. Your auditor does not need access to PhronEdge to prove the policy is authentic. They need the public key and a verification script.

Cryptographic specification

PropertyValue
AlgorithmECDSA P-256 (also called secp256r1)
Hash functionSHA-256
JWS algorithm nameES256
Signature encodingDER, hex-encoded
Canonical JSONKeys sorted alphabetically, no whitespace

ECDSA P-256 is the most widely supported asymmetric signature algorithm. It works with AWS KMS, GCP KMS, Azure Key Vault, and every mainstream cryptography library on every major platform.

How signing works

When you sign a policy through the Policy Builder, the CLI, or the API, the Brain performs this sequence:

  1. 1.Policy declarations are validated against applicable regulatory frameworks
  2. 2.If compliant, the Brain builds the canonical policy document (all fields resolved, sorted, canonicalized)
  3. 3.The canonical document is serialized to JSON with sorted keys and no whitespace
  4. 4.The JSON bytes are SHA-256 hashed
  5. 5.The hash is signed with your tenant's active ECDSA P-256 private key
  6. 6.The signature is attached as a phronedge_signature field
  7. 7.Credentials are issued to each agent in the policy
  8. 8.Events are anchored to the SHA-256 audit chain

The private key never appears in memory outside the signer backend. When your signer is aws_kms, gcp_kms, or azure_kv, the signature is computed inside the HSM. PhronEdge holds only the public key for verification.

Signature format

Every signed artifact contains a phronedge_signature field:

JSON
{
  "phronedge_signature": {
    "algorithm": "ES256",
    "key_id": "v2",
    "value": "3045022100bfa2fd722d758f9201d1d8fcb0aa859b95e122b80b865ddea99856dc945e6348..."
  }
}
FieldDescription
algorithmAlways ES256 (ECDSA P-256 with SHA-256)
key_idWhich version of the tenant key signed this artifact
valueHex-encoded DER signature over the canonical JSON

Canonical JSON

Before signing, the payload is converted to a deterministic byte representation:

  • Keys sorted alphabetically at every nesting level
  • Values rendered with no whitespace
  • UTF-8 encoding
  • These fields excluded from the payload before signing: phronedge_signature, anchor_hash, anchor_tx

Example canonicalization:

Input:

JSON
{
  "name": "claim_lookup",
  "permissions": ["read", "write"],
  "phronedge_signature": {}
}

Canonical form (what gets signed):

{"name":"claim_lookup","permissions":["read","write"]}

Every verifier that implements the same canonicalization produces the same bytes. Every verifier gets the same result.

Key management

Each PhronEdge tenant has its own ECDSA P-256 key pair. Private key operations happen only in the signer backend you configured. The public key is freely published.

Key lifecycle:

  1. 1.First sign. The very first policy sign creates the key in your signer backend automatically. Keys are always named v1 for the first version.
  2. 2.Normal operation. All signs use the current active key.
  3. 3.Rotation. A new version is created (v2, v3, and so on). The previous version is archived.
  4. 4.Verification. Archived keys remain accessible at the public key endpoint so previously issued credentials can still be verified.

The developer, CISO, or platform team never downloads, copies, or manually handles a private key. Key handling is a signer backend responsibility.

Key rotation

Rotate keys on your organization's normal key-rotation cadence, or immediately if compromise is suspected. Rotation is a Console-only operation.

Rotate through the Console:

  1. 1.Go to phronedge.com/brain
  2. 2.Open Settings in the sidebar
  3. 3.Click Security
  4. 4.Click Rotate Signing Key
  5. 5.Confirm

The Console performs the rotation through your signer backend. The new key is created, the previous key is archived, and a KEY_ROTATED event is anchored to the audit chain.

What happens on rotation:

ArtifactEffect
Currently active credentialsRemain valid. Verified under the previous (now archived) key.
Previously signed policiesRemain valid and verifiable indefinitely (archived keys stay published).
New policy signsUse the new key.
Re-signs and amendmentsUse the new key.

No downtime. No agent disruption. Existing signatures continue to verify.

Public key endpoint

Every tenant exposes a public key discovery endpoint at:

GET /.well-known/phronedge/{tenant_id}/keys.json

No authentication required. Anyone can fetch it. This is the endpoint your auditor, your regulator, or any third party uses to verify your signatures without PhronEdge being in the loop.

Response:

JSON
{
  "tenant_id": "tn_52447d402c904055",
  "keys": [
    {
      "kid": "v2",
      "alg": "ES256",
      "use": "sig",
      "status": "active",
      "created_at": "2026-03-15T12:00:00Z",
      "pem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...\n-----END PUBLIC KEY-----"
    },
    {
      "kid": "v1",
      "alg": "ES256",
      "use": "sig",
      "status": "archived",
      "created_at": "2026-01-10T09:00:00Z",
      "pem": "-----BEGIN PUBLIC KEY-----\nMFkw...\n-----END PUBLIC KEY-----"
    }
  ]
}

Archived keys remain in the response so that previously signed artifacts remain verifiable.

Independent verification

The full value of signing comes from the ability to verify without PhronEdge. This is what makes the audit trail enterprise-grade.

Python example

Complete verification script. Paste it into a new file, set the tenant_id and the credential variable, run.

Python
import requests, json
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.exceptions import InvalidSignature

# 1. Fetch the tenant's public keys (no auth)
tenant_id = "tn_your_tenant_id"
r = requests.get(
    f"https://api.phronedge.com/.well-known/phronedge/{tenant_id}/keys.json",
    timeout=10,
)
r.raise_for_status()
keys = r.json()["keys"]

# 2. The credential you want to verify (from /auth/credential or exported policy)
credential = {
    "credential_id": "cred-claims-investigator-3d8ecfb6",
    "agent_id": "claims-investigator",
    "tenant_id": "tn_52447d402c904055",
    "tier": "T2",
    "jurisdiction": "DE",
    "permitted_tools": {"claim_lookup": {"permissions": ["read"], "jurisdictions": ["DE"]}},
    "policy_hash": "6b890f03...",
    "expires_at": 4930068705,
    "phronedge_signature": {
        "algorithm": "ES256",
        "key_id": "v2",
        "value": "3045022100bfa2fd722d758f9201..."
    }
}

# 3. Find the public key used to sign
sig = credential["phronedge_signature"]
key_record = next((k for k in keys if k["kid"] == sig["key_id"]), None)
if not key_record:
    raise ValueError(f"Unknown key_id: {sig['key_id']}")

pub_key = serialization.load_pem_public_key(key_record["pem"].encode())

# 4. Reconstruct the canonical payload (exclude the signature and chain fields)
EXCLUDE = {"phronedge_signature", "anchor_hash", "anchor_tx"}
payload = {k: v for k, v in credential.items() if k not in EXCLUDE}
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")

# 5. Verify
try:
    pub_key.verify(
        bytes.fromhex(sig["value"]),
        canonical,
        ec.ECDSA(hashes.SHA256()),
    )
    print("Signature valid. Credential is authentic.")
except InvalidSignature:
    print("Signature invalid. Credential has been tampered with.")

The script makes exactly one HTTP call: to the public key endpoint. Verification itself is a local cryptographic operation.

Other languages

Any language with ECDSA P-256 support works. The verification contract is:

  1. 1.Fetch the public key PEM
  2. 2.Exclude phronedge_signature, anchor_hash, and anchor_tx from the payload
  3. 3.Serialize to canonical JSON (sorted keys, no whitespace)
  4. 4.Verify the DER signature over the canonical bytes using ECDSA with SHA-256

Reference implementations exist for Go (crypto/ecdsa), Node.js (crypto), Rust (ring or p256), and Java (java.security.Signature).

Tamper detection

Tampering is detected at three layers:

Layer 1: Signature verification on every call

Every tool call invokes Checkpoint 1, the Credential Validator. The credential's signature is verified with the tenant public key on every call. If the credential was modified, verification fails and the call is blocked.

Detection happens in microseconds. The function body never executes. Event anchored: CREDENTIAL_REJECTED.

Layer 2: Vault integrity check

At credential issuance, a SHA-256 hash of the vault copy is computed and anchored as a VAULT_CREDENTIAL_ISSUED event with a vault_hash field. Before serving a credential from the vault, the hash is recomputed and compared.

If the hashes do not match, a VAULT_INTEGRITY_BROKEN event is anchored (severity: CRITICAL) and the restore is blocked. The tampering event is permanently recorded in the chain and cannot be deleted. The credential is auto-restored from an immutable backup, and VAULT_CREDENTIAL_RESTORED is anchored after restoration.

Layer 3: Chain verification

Every audit event is SHA-256 linked to the previous event. The chain is verified on every read by the Observer. A manual audit can be triggered by phronedge chain verify.

If any event is modified after the fact, the chain breaks at that point. The break is mathematically detectable and pinpoints the exact event where tampering occurred.

What tamper detection catches

Real tests against the live system:

Tampering attemptResult
Change tier T1 to T2 in a credentialinvalid_signature at Checkpoint 1
Add a tool not in the original policyinvalid_signature at Checkpoint 1
Change allowed jurisdictions from DE to CNinvalid_signature at Checkpoint 1
Change expires_at to extend lifetimeinvalid_signature at Checkpoint 1
Replay an old credential after rotationVerification succeeds under archived key; policy hash mismatch blocks the call
Modify an audit event in the databaseChain verify detects break at exact event
Delete an audit event from the databaseChain verify detects missing link

Nothing requires human review. Cryptographic math detects tampering.

Audit chain

Every governance event is SHA-256 hashed and linked to the previous event. The chain is append-only.

How a chain entry is built:

  1. 1.Event data is combined with the previous event's hash
  2. 2.The combined payload is serialized to canonical JSON
  3. 3.SHA-256 digest is computed
  4. 4.The digest (first 16 hex characters) is stored as the event hash
  5. 5.The hash and prev_hash are written atomically with the event

Genesis event has prev_hash = "GENESIS".

Chain verification:

Shell
phronedge chain verify

The CLI walks the chain from the latest event backwards, recomputes every hash, and confirms every link.

In the Console, the Observer displays live chain integrity. The field chain_valid: true stays green as long as no break is detected.

Regulatory context

The signing surface maps to named regulatory requirements:

RegulationArticleWhat signing satisfies
GDPRArt. 5(1)(f)Integrity and confidentiality of processing
GDPRArt. 30Records of processing activities (signed and tamper-evident)
GDPRArt. 32Security of processing (cryptographic signatures, integrity checks)
EU AI ActArt. 12Record-keeping (signed, hash-chained audit log)
EU AI ActArt. 15Accuracy, robustness, cybersecurity (tamper detection)
ISO 42001Clause 9.1Monitoring, measurement, analysis, evaluation
SOC 2CC6.1Logical and physical access controls (cryptographic authentication)
SOC 2CC7.1System monitoring (immutable audit)
SOC 2CC7.3Incident response (tamper-evident logs)
DORAArt. 11Response and recovery (immutable evidence)

Your auditor can produce a verification script, run it against any credential, and have mathematical proof.

Next steps

Previous
Compliance Matrix
Next
Threat Model