This specification describes a mechanism for issuers of verifiable credentials to publish indexed lists of compact cryptographic witnesses. Verifiers can use these lists to confirm that a [=verifiable credential=] was genuinely issued by the claimed issuer — as distinct from a credential forged by an attacker who has compromised the issuer's signing key. Because the witness list is itself a verifiable credential, it can be signed with a different algorithm from the protected credentials, including post-quantum signature schemes. This makes the mechanism particularly valuable in scenarios where a conventional signing key has been compromised by a quantum-capable adversary.
This work is complementary to the [[[VC-DI-QUANTUM-RESISTANT]]] work and is motivated by the same threat: compromise of a conventional signing key by a cryptographically relevant quantum computer. Those cryptosuites let issuers secure new proofs with post-quantum signatures; this specification lets an issuer establish post-quantum-backed authenticity for credentials that were, or will be, signed with conventional quantum-vulnerable algorithms, by publishing a witness list that is itself signed with a post-quantum scheme.
The specification defines a data model and algorithms for three usage
modes: an implicit mode that piggybacks on an existing
[[[VC-BITSTRING-STATUS-LIST]]] without
modifying the protected credential; an explicit mode that adds a
credentialStatus entry to the protected credential; and a
standalone mode for use cases that define their own index
allocation.
[=Verifiable Credentials=] are often secured with digital signatures over their contents. If the issuer's private signing key is compromised — whether through an infrastructure breach or through advances in cryptanalysis such as the development of a cryptographically relevant quantum computer — an attacker can forge credentials that are indistinguishable from those created by the issuing party using standard signature verification alone.
This specification provides a mechanism similar to [[[VC-BITSTRING-STATUS-LIST]]] that allows an issuer to assert what subset of the credentials issued with their private key material they actually issued. This takes the form of a witness list that the issuer publishes alongside their credentials. For each credential issued, the issuer computes a short cryptographic witness derived from a random seed and the credential's position in the list. A verifier who fetches the list can confirm that the witness of the credential they are verifying matches the witness published by the issuer.
The witness list is itself a verifiable credential and may be signed using a different digital signature algorithm than the credentials on the list. Importantly, the witness list may be signed with a quantum-resistant digital signature, such as those in the [[[VC-DI-QUANTUM-RESISTANT]]] specification. In this way, an issuer can establish quantum-resistant authenticity for credentials issued with conventional and non-quantum-resistant digital signatures.
This specification is complementary to the [[[VC-DI-QUANTUM-RESISTANT]]]. Both are responses to the same threat — compromise of conventional signing keys, in particular by a cryptographically relevant quantum computer. Quantum-resistant cryptosuites address the threat going forward, producing proofs that remain unforgeable in a post-quantum setting. A witness list addresses the installed base and the transition period: it lets an issuer vouch, under a post-quantum signature, for credentials that were already issued — or that must continue to be issued — under conventional signatures, without re-issuing them. The two mechanisms are intended to be used together.
At [=verifiable credential=] issuance time, an issuer uses the credential data to create a cryptographic witness. A set of these witnesses are combined with a random seed, packed into an array, and published together as a witness list credential. This credential is signed — optionally with a post-quantum algorithm — and published for verifiers to access.
At verification time, a verifier obtains the witness list credential, re-derives the witness for the credential they are verifying using the published random seed and the credential data, and compares it to the value stored at the credential's witness index. A mismatch indicates that the credential was not issued by the claimed party.
A conforming document is any concrete expression of the data model that complies with the normative requirements in Section . A conforming processor is any algorithm realized as software and/or hardware that generates or consumes a conforming document according to the normative requirements in Sections and .
Conforming processors MUST produce errors when non-conforming documents are consumed.
The following terms are used throughout this document. Terms defined in [[VC-DATA-MODEL-2.0]] are used with the meanings defined there.
SHA-256(seedBytes || credentialHash) and taking the most
significant witness length bits of the output.
WitnessListCredential
whose credentialSubject is of type WitnessList
and contains the witness list properties.
WitnessListEntry that MAY appear in the
credentialStatus of a protected verifiable credential
in explicit mode, linking the credential to its position in a
witness list credential, which it references through an explicit
witnessListCredential property.
u) packed bit array of all witnesses in a
witness list, typed as
https://w3id.org/security#multibase in the JSON-LD context.
credentialStatus entry,
and the status list index is reused as the witness index.
credentialStatus that
directly references the witness list credential and
witness index.
A witness list credential is a
verifiable
credential whose credentialSubject is of type
WitnessList and carries the witness list properties
(witnessSeed, witnessLength,
witnessCount, and encodedWitnesses) directly. Issuers SHOULD use a
separate key pair — and MAY use a post-quantum signature scheme — to
sign the witness list credential, since the protection it provides
is only meaningful if it can be verified after the ordinary signing key
has been compromised.
The type array of the credential MUST include
WitnessListCredential.
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://w3id.org/vc-forgery-defense/v1rc1"
],
"id": "https://issuer.example/witnesses/1",
"type": ["VerifiableCredential", "WitnessListCredential"],
"issuer": "did:example:issuer",
"validFrom": "2024-01-15T00:00:00Z",
"credentialSubject": {
"type": "WitnessList",
"witnessSeed": "26ea4078-968d-4d98-aba7-695610c0dfb6",
"witnessLength": 128,
"witnessCount": 131072,
"encodedWitnesses": "uQYF16SSSbR_LrMZFbFvbGkn8B7ggEZtov3KVL..."
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-rdfc-2022",
"comment": "MAY use a post-quantum cryptosuite here",
"verificationMethod": "did:example:issuer#pq-key-1",
"proofPurpose": "assertionMethod",
"proofValue": "z..."
}
}
When WitnessListCredentials are distributed by an issuer
via a space-constrained medium (such as over the web for large credentials),
it is RECOMMENDED that they are CBOR-LD
encoded and transmitted as bytes.
The credentialSubject of a WitnessList
MUST contain the following properties directly.
u), typed as
https://w3id.org/security#multibase, representing the
packed bit array of all witnesses. The witness for
witness index i occupies bits
(i − 1) × witnessLength through
i × witnessLength − 1 (inclusive) in
most-significant-bit-first order.
When used in explicit mode, a protected verifiable
credential MAY include a witness list entry in its
credentialStatus array (or as its sole
credentialStatus value).
The type of the entry MUST be WitnessListEntry.
Following the pattern of [[[VC-BITSTRING-STATUS-LIST]]]'s
statusListCredential property, the entry references its
witness list credential through an explicit
witnessListCredential property whose value is the URL of
that credential. The entry's id, if present, is an OPTIONAL
unique identifier for the entry and is not used to locate the list.
statusListCredential in [[[VC-BITSTRING-STATUS-LIST]]].
witnessCount.
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://w3id.org/vc-forgery-defense/v1rc1"
],
"id": "https://issuer.example/credentials/1234",
"type": ["VerifiableCredential", "ExampleDegreeCredential"],
"issuer": "did:example:issuer",
"validFrom": "2024-01-15T00:00:00Z",
"credentialStatus": [
{
"id": "https://issuer.example/status/1#94567",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "94567",
"statusListCredential": "https://issuer.example/status/1"
},
{
"type": "WitnessListEntry",
"witnessListCredential": "https://issuer.example/witnesses/1",
"witnessIndex": 94567
}
],
"credentialSubject": {
"id": "did:example:holder",
"degree": {"type": "BachelorDegree", "name": "Computer Science"}
},
"proof": { ... }
}
The following algorithm generates a witness list for a set of N credential hashes. Inputs: an array of N hashes, in order of their desired witnessIndex values, and the witness length b (default 128). Outputs: a witness seed and encoded witnesses.
(i − 1) × b, most-significant-bit first.
u
[[MULTIBASE]].
The following algorithm wraps the output of Generate Witness List into a signed verifiable credential.
type including WitnessListCredentialcredentialSubject.type set to WitnessListcredentialSubject.witnessSeed
set to seedcredentialSubject.witnessLength
set to bcredentialSubject.witnessCount
set to NcredentialSubject.encodedWitnesses
set to encodedWitnessesThe following algorithm verifies that a given credential's witness is valid. Inputs: the resolved witness list credential, a credential hash, and the witness index i.
credentialSubject.
.witnessSeed..witnessLength..witnessCount..encodedWitnesses.
u. If not, return an error.
(i − 1) × b through
i × b − 1 from data, most-significant-bit first.
Let this be storedW.
valid. Otherwise return invalid.
In implicit mode, the protected credential is unmodified. No
reference to the witness list appears in the credential itself. The
verifier locates the witness list using out-of-band information —
typically derived from the credential's existing
credentialStatus entry — and reuses the status list index
as the witness index.
This mode REQUIRES that:
BitstringStatusListEntry
(or equivalent) whose index corresponds 1:1 to a position in the
witness list.
// Protected credential has:
// credentialStatus.statusListCredential = "https://issuer.example/status/1"
// credentialStatus.statusListIndex = "94567"
// Verifier discovers witness list URL from issuer metadata:
// GET https://issuer.example/.well-known/witness-lists
// -> { "https://issuer.example/status/1":
// "https://issuer.example/witnesses/1" }
// Witness index = 94567 (same as status list index)
// Run: Verify a Credential Witness with i = 94567
In explicit mode, the protected credential contains a
witness list entry in its credentialStatus. The
entry's witnessListCredential property gives the
witness list credential URL, and witnessIndex
specifies the 1-based position in the list. No discovery mechanism is
required.
Explicit mode MAY coexist with other credentialStatus
entries (such as a BitstringStatusListEntry for revocation).
If both are present, witnessIndex need not equal the
status list index.
credentialStatus entry of type
WitnessListEntry.
witnessListCredential property.
witnessIndex as i.
In standalone mode, the witness list is used independently of any status list infrastructure. The allocation of witness indices to credentials is defined by the specific application or use case. The witness list credential MAY be referenced from credentials, application-layer records, or external registries — the reference mechanism is out of scope for this specification.
A quantum-capable adversary can recover the private key corresponding to a conventional (e.g., ECDSA) public key. If the issuer has signed the witness list credential with a post-quantum signature scheme, a verifier can still trust the witness list even after the signing key for the credentials on it has been broken, provided the post-quantum key remains secure.
Issuers SHOULD sign the witness list credential with a post-quantum signature scheme whenever the credentials it covers are of significant value or long validity.
The witness seed appears in the witness list credential's
credentialSubject and
is visible to any party who obtains the witness list credential.
This is by design — verifiers need the seed to recompute witnesses.
Issuers who wish to restrict verification to a specific set of verifiers
SHOULD limit distribution of the witness list credential accordingly.
A forged credential at index i passes witness verification with probability 2−b, where b is the witness length. At the default of b = 128, this probability is approximately 2.9 × 10−39.
Implementations MUST NOT accept values of b less than 32. A minimum of 64 is RECOMMENDED for credentials of significant value.
Witnesses SHOULD be computed in a way that is generally collision resistant, but they MUST be computed in a way that is second-preimage resistant.
Verifiers MUST verify the proof on the witness list credential before trusting its contents. Verifiers SHOULD cache resolved witness list credentials for their validity period to reduce network exposure and denial-of-service risk.
Using witnessCount exactly sized to how many issued [=verifiable credentials=] the list covers leaks information about issuer processes in two ways:
To mitigate the first leak, issuers SHOULD use fixed witnessCount values, such that across all witness lists the total number of witnesses does not change, with arbitrary data witness placeholders being replaced by witnesses that correspond to VCs over time.
This is sufficient to mitigate the first leak - however, an attacker still could track how many witnesses are "real" and how many VCs are being issued by tracking what witness values change over time. To address this, issuers MUST regenerate the random witness seed each time they update a WitnessListCredential.
An attacker who can replace the published witness list credential with a modified version can bypass the anti-forgery mechanism. Issuers MUST publish witness list credentials over integrity-protected channels and SHOULD use short validity periods combined with frequent re-issuance.
The issuer is the sole author of the witness list credential, and is REQUIRED to regenerate the witness seed each time the list is updated (see ). An issuer can therefore repudiate a credential it previously issued by publishing a new witness list credential in which that credential's witness is absent or altered: a verifier who recomputes the witness against the latest list obtains a mismatch and rejects an otherwise genuine credential. Unlike forgery, this behavior is available to the legitimate issuer and cannot be prevented by the mechanism alone, because every published list is a validly signed credential.
Relying parties that require evidence of issuance to survive later
repudiation SHOULD retain (cache or archive) the
witness list credentials they relied upon, together with their
proofs and validity periods. Because each
witness list credential is itself a signed
verifiable credential with a validFrom value, a
credential that verified against a list published before the
repudiation can still be shown to have been genuine as of that earlier
list — the cached, signed list is durable evidence that the issuer
attested to the credential at that time. Independent archival,
notarization, or transparency-log witnessing of published lists
strengthens this guarantee by making the issuer's published history
non-repudiable.
The witnessCount value reveals the maximum number of
credentials in a batch. Issuers MUST use large set sizes (at least
131072 entries) so that individual holders cannot be identified by their
position in the list. All slots in such a set must be filled, whether or
not the hash used to create the witness corresponds to a
verifiable credential.
Fetching the witness list credential creates a network request that may reveal to the host that verification is occurring. Verifiers SHOULD cache witness list credentials and MAY use privacy-preserving network transports when fetching lists. Issuers SHOULD serve witness lists through CDNs or content-addressed systems to reduce correlation capability.
The examples in this document refer to the v1rc1 version of a `vc-forgery-defense` JSON-LD context. This context is reproduced below for reference.
{
"@context": {
"@protected": true,
"id": "@id",
"type": "@type",
"WitnessListCredential": "https://w3id.org/vc-forgery-defense#WitnessListCredential",
"WitnessList": "https://w3id.org/vc-forgery-defense#WitnessList",
"WitnessListEntry": "https://w3id.org/vc-forgery-defense#WitnessListEntry",
"witnessSeed": {
"@id": "https://w3id.org/vc-forgery-defense#witnessSeed"
},
"encodedWitnesses": {
"@id": "https://w3id.org/vc-forgery-defense#encodedWitnesses",
"@type": "https://w3id.org/security#multibase"
},
"witnessLength": {
"@id": "https://w3id.org/vc-forgery-defense#witnessLength",
"@type": "http://www.w3.org/2001/XMLSchema#integer"
},
"witnessCount": {
"@id": "https://w3id.org/vc-forgery-defense#witnessCount",
"@type": "http://www.w3.org/2001/XMLSchema#integer"
},
"witnessIndex": {
"@id": "https://w3id.org/vc-forgery-defense#witnessIndex",
"@type": "http://www.w3.org/2001/XMLSchema#integer"
},
"witnessListCredential": {
"@id": "https://w3id.org/vc-forgery-defense#witnessListCredential",
"@type": "@id"
}
}
}