Skip to content
Browse documentation

OPRP 0.1.0-dev.11 · Draft documentation

Credential authorities, status and requester rotation

This describes the executable experimental trust profile in 0.1.0-dev.3. Each participant independently admits authorities and chooses their authority limits. Convey supplies no required root, registration service, or runtime directory. The format borrows PKI concepts but is a custom ML-DSA profile, not an X.509 certificate chain or a complete public vendor-enrollment system. RFC 5280 remains a reference for the broader issuance and trust model.

Explicit authority boundaries

TrustStore(policies, path) accepts policies keyed by issuer. Each specifies publicKey, allowed kinds, subjectPrefixes, permitted roles by kind, maximumLifetime, and maximumStatusAge. The prototype requires namespace prefixes ending in : to avoid accidental partial-prefix matches. Its synthetic identities use URNs. A future public profile must define organization/deployment identifiers and domain or namespace control evidence; the demo namespace is not such evidence.

The development Host authority can issue only host identities under urn:oprp:demo:. The application/person authority can issue only application and person identities there, with an explicitly allowed application role. A correctly signed credential outside those limits is rejected. A configured role is a permitted authority assertion, not a patient permission. Emergency eligibility remains a Host-policy decision.

Credential verification checks signature, namespace/kind/roles, validity and maximum lifetime, both key fingerprints, current issuer status, and expected identity/kind. Revocation and disabled-issuer state are persisted when a database path is supplied. Host and SDK operators own their trust databases and private-key stores. The reference Host does not close a caller-owned TrustStore; the caller must close it separately.

Authenticated status and bounded freshness

A signed status document contains payload and signature. The compact payload contains exactly:

issuer, sequence, issued_at, next_update,
revoked_serials, revoked_signing_kids, revoked_encryption_kids

The authority signs ASCII OPRP-STATUS-0.1. followed by the encoded payload using ML-DSA-65. ingestStatus(document, now) verifies the signature, admitted issuer, bounded validity, nonnegative safe integer sequence/times, and bounded unique revocation lists. The status must already be effective and not have reached next_update. Status lifetime cannot exceed that issuer's configured maximum; the demonstration uses 600 seconds.

Each trust database durably stores the highest accepted sequence and signed document. Lower sequences fail. A higher sequence cannot regress issue time. The same sequence is accepted only for the identical encoded payload; a randomized signature over that same payload is harmless. A different payload at the same sequence is equivocation and fails. All listed revocations are added to a durable deny set. Omitting an old revocation from a later document cannot reactivate it.

Serial revocation stops that credential. Signing-key or encryption-key revocation also stops later credentials reusing that key. Local revoke(issuer, kind, value) and disableIssuer(issuer) are trusted operator controls, not requester operations. Issuer disablement persists and overrides all credentials and historical archive evidence under that issuer. There is deliberately no automatic unrevocation API.

Missing or expired status fails closed before payload decryption. Operators must obtain and ingest updates from their chosen authority through a bounded, authenticated status worker; no public status-download endpoint, worker, trust-list distribution or CA recovery service is implemented yet. The synthetic fixture signs fresh status when its test clock advances. That is test infrastructure, not an online availability strategy. Revocation can be unknown until the next accepted update; freshness bounds that interval rather than eliminating it.

Renewal versus key rotation

Renewal with the same identity and both keys creates a new serial/validity interval. It does not create new patient authority or extend the grant. The renewal test revokes the original serial, replaces the SDK credential with a newly issued same-key credential, and successfully reads and refreshes under the original bounded grant. A revoked key cannot be repaired by issuing another serial. Private-key loading, renewal automation, issuer-key transitions and lost-key recovery remain operational work.

Planned requester rotation changes both signing and encryption keys. It requires current credentials and proof of control of both the old and new signing keys. makeRotationProof creates a payload binding rotation_id, destination host, requester subject, both old/new key IDs, issued_at and expires_at. Both identities sign ASCII OPRP-ROTATION-0.1. plus payload. The proof lives at most 120 seconds and is transmitted in a rotate_requester exchange signed and encrypted using the new identity.

Each Host pins a requester's current key pair durably. A second credential for the same subject cannot acquire new preauthorized grants, adopt pending requests, or use old grants merely by presenting a new key. It must complete rotation. When upgrading older prototype storage, existing active grants constrain the first accepted signing key.

The Host validates both credentials/statuses, subject, Host, time, signatures, old pin and new caller. One transaction rebinds the requester's grants and authorization requests, replaces the current pin, and retires old refresh credentials/recovery receipts. Grant scopes, original end times, actor context and active/revoked state remain unchanged. Revoked or expired grants stay unusable.

The rotation has a durable receipt keyed by requester and rotation ID. Retrying the identical proof using the still-current new keys can recover the receipt after Host restart or old-proof expiry. That retry does not execute rotation again and does not require the now-retired old credential to remain valid. Altered content or a superseded new key fails. Initially expired proofs and proofs without both signatures fail.

Use replace_token with the existing grant_id after rotation. It checks current caller, active grant, representative authority and original end time, then issues a short JWT under the current Host key. A refresh credential is issued only if that grant already permitted refresh. The SDK method stores the replacement and clears pending old refresh state. Repeated replacement/polling currently can create additional refresh families; lifecycle refinement and cross-process coordination remain gates. Replacement is an explicit SDK method rather than an automatic lost-key recovery flow.

Each Host's pin is local. Coordinate planned rotation with every relevant Host and retire old authority credentials/keys when the transition finishes. No automatic cross-Host broadcast exists. Compromise or loss of the old private key needs a separately authenticated recovery or new-authorization process; possession of a new CA credential alone cannot bypass an existing pin. Host-key rotation, overlapping receiver-key retention and address-authority rotation are separate unfinished profiles.

Offline archive evidence

Archives carry their source Host credential and its signed status at creation. Verification evaluates those historical documents at created_at under independently pinned authority policies, while still honoring all locally known revocations and disabled issuers. Historical evidence is never ingested into the live cache and cannot roll its sequence back. Current status need not be fetched to read an offline backup.

A signed creation time is not an independently notarized timestamp. An offline reader cannot know later compromise or revocation without newer trust information, and known compromise makes the default verifier fail closed. Long-term evidence, timestamping, compromise adjudication and patient recovery exceptions need a reviewed profile. See portability.

Verification and independent implementation

The lifecycle tests cover issuer boundaries, signed-status forgery/staleness/rollback/equivocation, durable revocation and issuer disablement, renewal, dual-key rotation, retired refresh credentials, unchanged scopes/end times, restart and retry. The independent Python codec verifies supplied authority policies and signed fresh status using PQClean, but does not implement a durable status ledger or rotation service. It remains a cryptographic interoperability harness, not a conformant full SDK. Structural schema.

View source

Documentation follows the experimental reference implementation.