Summary
A DIDComm v2 protocol for publishing cryptographically secure app manifests using zero-knowledge proofs (PoE) that bind packages to signing certificates, publisher DIDs, and secure execution environments. Prevents fake signatures and key compromise attacks through TEE-backed attestation.
Summary
The Publisher Authority Manifest (PAM) 1.0 protocol enables Registered Entities (publishers) to publish cryptographically secure manifests that authoritatively bind their application packages to signing certificates, publisher DIDs, and secure execution environments. PAM leverages the Proof of Execution (PoE) 1.0 protocol to provide verifiable, tamper-proof evidence that manifests are created in trusted environments with proper authorization, preventing fake signatures and key compromise attacks.
Verifiers (SDKs, app stores, MDMs) fetch and validate both the manifest structure and its embedded PoE proof before installation or during runtime to establish trust.
Goals
- Bind app packages to publisher DIDs and signing certificates with cryptographic proofs
- Prevent key compromise attacks using TEE-backed PoE proofs
- Support multiple platforms (Android, iOS, web extensions, desktop, server)
- Enable both HTTPS-based (
.well-known) and DIDComm-based manifest distribution - Provide certificate rotation with grace periods
- Support revocation of compromised certificates and packages
- Integrate device attestation (Play Integrity, App Attest, TPM) into proofs
- Maintain privacy through zero-knowledge proofs
Roles
-
publisher (Registered Entity): Owns the domain and app, controls a Publisher DID, hosts PAM at
/.well-known/app-auth.json. Generates PoE proofs during manifest creation. -
verifier: Client SDK/app or service that validates PAM structure, PoE proofs, and compares to observed package metadata.
-
attester (optional): TEE-backed secure execution environment (SGX, TrustZone, Secure Enclave) that generates PoE proofs on behalf of the Publisher, proving the manifest was created with proper authorization and device attestation.
-
re-agent (optional): Serves PAM via DIDComm for pairwise/air-gapped/offline flows.
Transport & Security
HTTPS Path
Verifier MUST GET https://<domain>/.well-known/app-auth.json over TLS 1.2+.
The payload MUST be a JSON structure containing the manifest data and an embedded PoE proof.
PoE Integration
The manifest MUST include a PoE proof generated by the program_id didcomm.pam.legitimacy.v1 that cryptographically proves:
- The manifest was created in a secure execution environment
- The Publisher had proper authorization at creation time
- Device/platform attestation passed (Play Integrity, App Attest, or equivalent)
- The manifest content integrity is bound to the proof via
context_hash
Domain ↔ DID Binding
Publisher MUST publish a DID Configuration (Domain Linkage Credential) at /.well-known/did-configuration.json. Verifiers MUST validate domain linkage before trusting the manifest.
DIDComm Path (Optional)
pam/fetch over DIDComm v2 authcrypt. Content SHOULD be encrypted (JWE). The PoE proof provides signature guarantees, so additional JWS wrapping is redundant.
Clock & Freshness
Verifiers MUST enforce nbf/exp in the PAM payload, validate the PoE proof's nonce freshness, and MUST fail closed on clock-skew greater than policy (default 5 minutes).
Data Model
Top-Level Structure
{
"iss": "did:example:publisher",
"sub": "https://example.com",
"jti": "pam-2b0d-…",
"iat": 1731350400,
"nbf": 1731350400,
"exp": 1734032400,
"pam": {
"version": "1.0",
"publisher": {
"did": "did:example:publisher",
"org": "Example Corp",
"contact": "[email protected]"
},
"artifacts": [ /* see Artifacts section */ ],
"policies": { /* see Policies section */ },
"rotation": { /* see Rotation section */ },
"revocations": { /* see Revocations section */ },
"evidence": { /* optional */ }
},
"poe_proof": {
"program_id": "didcomm.pam.legitimacy.v1",
"result": "pass",
"public": {
"schema": "didcomm.pam.legitimacy.v1/public@1",
"nonce": "0x<32-byte-hex>",
"context_hash": "0x<32-byte-hex>",
"session_id": "0x<16-byte-hex>",
"publisher_did": "did:example:publisher",
"domain": "https://example.com",
"manifest_hash": "0x<32-byte-hex>",
"timestamp": 1731350400,
"vk_hash": "0x<32-byte-hex>"
},
"zk": {
"scheme": "stark",
"circuit_id": "didcomm-pam-legitimacy-v1",
"vk_hash": "0x<32-byte-hex>",
"proof_b64": "base64(urlsafe)…"
},
"attestation": {
"platform": "android",
"attestation_type": "play_integrity",
"attester_did": "did:example:attester",
"attestation_timestamp": 1731350400
}
}
}
Artifacts (per platform package binding)
{
"platform": "android|ios|webext|desktop|server",
"package_id": "com.example.app",
"signing": {
"cert_sha256": ["ABCD…", "…"],
"sig_alg": "SHA256withRSA|ECDSA_P256"
},
"distribution": {
"channels": ["play", "appstore", "direct", "chrome_web_store"],
"urls": ["https://play.google.com/…", "https://example.com/downloads/app.apk"]
},
"versions": {
"min": "1.2.3",
"max": "2.*",
"pinned": []
},
"integrity": {
"hash_alg": "sha256",
"apk_digest": [],
"sri": []
},
"policy_hints": {
"min_sdk": 26,
"permissions_max": ["CAMERA", "BLUETOOTH"],
"risk_flags": ["needs_camera_for_liveness"]
},
"attestation_policy": {
"android": {
"play_integrity": {
"require_cts_profile_match": true,
"require_basic_integrity": true,
"package_name": "com.example.app",
"certificate_digests": ["ABCD…"]
}
},
"apple": {
"app_attest": { "require_pass": true }
}
},
"metadata": {
"support_url": "https://example.com/security",
"privacy_policy": "https://example.com/privacy"
}
}
Policies
{
"install": {
"must_match_signing": true,
"must_match_distribution_channel": false
},
"runtime": {
"require_attestation": true,
"refresh_interval_hours": 24
},
"verification": {
"allow_clock_skew_seconds": 300,
"require_strict_tls": true
}
}
Rotation
{
"next_manifest_at": "https://example.com/.well-known/app-auth-next.json",
"grace_period_hours": 168,
"allowed_overlap_cert_sha256": ["…"]
}
Revocations
{
"cert_sha256": ["REVOKED_FINGERPRINT…"],
"package_ids": ["com.example.badapp"],
"advisories": [
{
"id": "ADV-2025-0001",
"severity": "high",
"reason": "supply-chain incident date …",
"effective": "2025-11-01T00:00:00Z"
}
]
}
Evidence (Optional)
Pointers to transparency logs, notarization receipts, or signed build attestations (SLSA, Sigstore).
PoE Program Specification
Program ID: didcomm.pam.legitimacy.v1
Purpose: Prove that a PAM was created by an authorized Publisher in a secure execution environment with valid device attestation.
Private Inputs (not revealed in proof)
- Publisher's signing keys
- Device attestation tokens (Play Integrity response, App Attest assertion, TPM quote)
- Authorization credentials (MFA tokens, access control proofs)
- Environment secrets
Public Inputs (revealed in proof)
publisher_did: The Publisher's DIDdomain: The domain hosting the PAMmanifest_hash: SHA256 of the entire pam objecttimestamp: Unix timestamp of manifest creationnonce: Fresh random nonce (prevents replay)context_hash: SHA256(iss||sub||jti||pam) binding proof to this specific manifestsession_id: Unique session identifier
Proof Requirements
- MUST verify that the private signing key corresponds to
publisher_did - MUST verify that the device attestation is valid and current (not expired, not revoked)
- MUST verify that the
manifest_hashmatches SHA256(pam object) - MUST verify that
context_hashcorrectly binds all public fields - MUST verify that authorization credentials are valid at timestamp
- Result MUST be
"pass"for the manifest to be accepted
Circuit Constraints
- Device attestation timestamp MUST be within 5 minutes of manifest timestamp
- Publisher DID MUST match the attested identity
- All cryptographic signatures MUST verify correctly
Validation Algorithm (Verifier)
Step 1 - Fetch: GET /.well-known/app-auth.json over HTTPS.
Step 2 - Domain Link: Fetch and verify DID Configuration at /.well-known/did-configuration.json to assert domain ↔ iss (Publisher DID).
Step 3 - PoE Registry Check:
- Verify program_id
"didcomm.pam.legitimacy.v1"is in the verifier's trusted program registry - Verify
vk_hashfrompoe_proof.zk.vk_hashmatches an allowed verifying key for circuit_id"didcomm-pam-legitimacy-v1" - Reject if program or
vk_hashis not in registry
Step 4 - PoE Proof Verification:
- Verify ZK proof using:
verify_zk(proof_b64, public, circuit_id, vk_hash) - Verify
context_hash = SHA256(iss||sub||jti||pam) - Verify
manifest_hash = SHA256(pam object) - Verify nonce is fresh (not previously seen, within acceptable time window)
- Verify timestamp is within
nbf/expbounds - Verify
result = "pass" - MUST reject if any verification fails
Step 5 - Freshness: Enforce nbf/exp timestamps in PAM payload.
Step 6 - Attestation Policy: Verify attestation.attestation_type matches required platform attestation (e.g., Play Integrity for Android, App Attest for iOS).
Step 7 - Artifact Match: Locate artifact matching observed platform, package_id, cert_sha256 (and optional version).
Step 8 - Policies: Enforce policies.install/runtime and attestation_policy requirements.
Step 9 - Rotation: If next_manifest_at present and within grace period, accept either manifest while overlapping certs are listed in rotation.allowed_overlap_cert_sha256.
Step 10 - Revocations: If any cert_sha256 or package_id matches revocations → BLOCK.
Decision
Return structured verdict:
- trusted: All checks passed, PoE proof valid, artifact matches
- untrusted: PoE proof invalid, revoked, or attestation failed
- unknown: Artifact not found in manifest or registry check failed
CRITICAL: Verifiers MUST fail closed. Any error in PoE verification MUST result in "untrusted" verdict.
Message Reference
All messages use DIDComm v2 with type = https://didcomm.org/pam/1.0/<message-name>, standard headers (id, thid, from, to, expires_time). Use authcrypt.
fetch
Message Type URI: https://didcomm.org/pam/1.0/fetch
Purpose: Request PAM manifest from Publisher or RE Agent.
From: verifier → publisher/re-agent
Body:
{
"domain": "example.com",
"nonce": "0x<32-byte-hex>"
}
The nonce parameter allows the Requester to request a fresh PoE proof bound to their specific verification session.
publish
Message Type URI: https://didcomm.org/pam/1.0/publish
Purpose: Deliver PAM manifest with PoE proof.
From: publisher/re-agent → verifier
Body:
{
"domain": "example.com",
"pam_payload": { /* full PAM structure with poe_proof */ },
"served_at": "2025-11-12T10:30:00Z"
}
MUST be authcrypt (DIDComm v2 encrypted). The embedded PoE proof provides authenticity, so additional JWS wrapping is not required.
Interactive PoE Generation (Advanced)
For high-security use cases, Requesters MAY invoke the PoE protocol directly:
- Send
poe/request-poewithprogram_id="didcomm.pam.legitimacy.v1" - Include
bind_to_contextwith nonce, context_hash, session_id - Receive
poe/submit-poewith the ZK proof - Construct the PAM manifest with the received proof
- Optionally cache the manifest at
/.well-known/app-auth.json
This flow allows real-time proof generation and verification for air-gapped or pairwise scenarios.
Error Codes
Error codes (using DIDComm problem-report):
auth/unauthorized: Publisher authentication faileddid-linkage/missing: DID Configuration not found or invalidpoe/invalid: PoE proof verification failedpoe/vk_unknown: Verifying key not in registrypoe/program_not_supported: Program ID not supportedexpired: Manifest or proof expiredcert/mismatch: Certificate doesn't match artifactrotation/not-yet-valid: Rotation window not yet activeattestation/failed: Device attestation verification failedtoo-large: Payload exceeds size limits
Security Analysis
Protection Against Key Compromise
Traditional JWS-only manifests are vulnerable to key compromise attacks. If an attacker obtains the Publisher's signing key, they can create fake manifests that pass JWS verification.
PAM with PoE integration prevents this attack vector:
Attack Scenario: Attacker steals Publisher's signing key
- Traditional PAM: Attacker signs fake manifest → passes verification ✗
- PoE-secured PAM: Attacker cannot generate valid PoE proof without:
- Access to the secure execution environment (TEE)
- Valid device attestation at signing time
- Authorization credentials (MFA, access controls)
- Ability to bind proof to current timestamp and fresh nonce
Result: Even with stolen keys, attackers cannot create valid manifests.
Replay Attack Prevention
The PoE proof binds to:
nonce: Prevents replaying old proofscontext_hash: Binds proof to specific manifest contentsession_id: Prevents cross-session replaytimestamp: Enforces temporal validity
Verifiers MUST maintain a nonce cache and reject previously-seen nonces within the validity window.
Device Attestation Integrity
The PoE circuit verifies device attestation inside the proof without revealing:
- Attestation tokens or responses
- Device identifiers or hardware fingerprints
- Authorization credentials
This provides strong device binding while preserving privacy.
Supply Chain Security
The combination of:
- PoE proof of secure signing environment
- Device attestation
- DID Configuration domain linkage
- Certificate pinning in artifacts[]
- Optional transparency log evidence
Creates a multi-layered defense against supply chain attacks, compromised build systems, and malicious package injection.
Threat Model
Protected Against:
- ✓ Stolen signing keys (requires TEE + attestation)
- ✓ Replay attacks (nonce + context binding)
- ✓ Manifest tampering (context_hash integrity)
- ✓ Unauthorized signers (authorization proof required)
- ✓ Compromised servers (attestation proves signing environment)
- ✓ Time-based attacks (timestamp binding + freshness checks)
Not Protected Against:
- ✗ Complete compromise of TEE/secure enclave
- ✗ Publisher's intentional malicious manifest creation
- ✗ Verifier implementation bugs
- ✗ Registry poisoning (verifiers must maintain secure registries)
Mitigation: Use trusted attester services, transparency logs, and multi-party verification for high-value applications.
Composition
| Supported Goal Code | Notes |
|---|---|
poe/programs |
PAM uses PoE program didcomm.pam.legitimacy.v1 |
Related Protocols
- Proof of Execution (PoE) 1.0: Core dependency for ZK proofs
- DID Configuration: Domain ↔ DID binding
- Report Problem 2.0: Error reporting
- Discover Features 2.0: Capability negotiation
References
- DIDComm Messaging Specification v2.0 — https://identity.foundation/didcomm-messaging/spec/v2.0/
- Proof of Execution (PoE) 1.0 — https://didcomm.org/poe/1.0/
- DID Configuration — https://identity.foundation/.well-known/resources/did-configuration/
- Play Integrity API — https://developer.android.com/google/play/integrity
- App Attest — https://developer.apple.com/documentation/devicecheck/validating_apps_that_connect_to_your_server
- SLSA — https://slsa.dev/
- Sigstore — https://www.sigstore.dev/
Endnotes
This protocol provides a foundation for establishing cryptographic trust in software distribution by combining DIDs, zero-knowledge proofs, and device attestation. It is designed to work alongside existing app distribution infrastructure while adding strong security guarantees against supply chain attacks.