§ AnonCreds Specification

Specification Status: v1.0 Draft

Latest Draft:

https://github.com/AnonCreds-WG/anoncreds-spec

Editors:

Participate:
GitHub repo
Commit history
Discord

§ Abstract

Th AnonCreds specification is based on the open source verifiable credential implementation of AnonCreds that has been in use since 2017 as part of the Hyperledger Indy open source project. The extensive use of AnonCreds around the world has made it a de facto standard for ZKP-based verifiable credentials, and this specification is the formalization of that implementation.

§ Status of This Memo

This is a pre-Internet Draft document.

This document is a product of the AnonCreds Working Group. It represents the consensus of the AnonCreds community.

Information about the current status of this document, any errata, and how to provide feedback on it may be obtained at https://github.com/AnonCreds-WG/anoncreds-spec.

This specifications is subject to the Community Specification License 1.0 available at https://github.com/CommunitySpecification/1.0.

If source code is included in the specification, that code is subject to the Apache 2.0 license unless otherwise marked. In the case of any conflict or confusion within this specification between the Community Specification License and the designated source code license, the terms of the Community Specification License shall apply.

§ Summary of the contents

TODO

Provide a textual overview of the sections. Table of contents not needed, since it is autogenerated in the left pane.

§ Introduction

AnonCreds ZKP verifiable credentials provide capabilities that many see as important for digital identity use cases in particular, and verifiable data in general. These features include:

The AnonCreds v0.1 Specification matches the existing Hyperledger Indy SDK (“libindy”) and Indy Credential Exchange (“cred-x”) implementations.

The next version of the specification (tentatively v1.0) will remove from the v0.1 specification any dependence on Hyperledger Indy by removing any requirements related to the storage of the objects used in AnonCreds, whether they be stored remotely on a “verifiable data registry” (including Hyperledger Indy) or in local secure storage.

§ Requirements, Notation and Conventions

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

§ Terminology

claim
A claim is a part of digital identity related to a subject. A claim can be attested by the identity subject itself, or it can be asserted by another entity.
Correctness Proof
TODO
TODO

Describe (key) Correctness Proof

credential

A credential is a set of claims about an identity subject. A verifiable credential is a tamper-proof credential whose authorship is cryptographically verifiable. An anonymous credential, also known as AnonCreds, is a verifiable credential that has privacy-preserving properties to enable data minimization and correlation resistance.

CRED_DEF

A CRED_DEF (short for “credential definition”, also known as CLAIM_DEF) contains data required for credential issuance (used by the issuer) as well as credential validation data (used by the holder and the verifier). A CRED_DEF is generated by the issuer before credential issuance and consists of two distinct but strongly related parts, the CRED_DEF_PUBLIC and CRED_DEF_PRIVATE.

A CRED_DEF_PUBLIC part of a CRED_DEF is a public object (e.g. on a ledger), that references a SCHEMA, references a DID of the issuer and can be written to the ledger by any issuer who intends to issue credentials based on that specific SCHEMA and has the proper permissions in doing so. A CRED_DEF_PUBLIC has to be accessible to all participants (issuers, holders, and verifiers). A SCHEMA is in a 1:n relation with CRED_DEF_PUBLIC, meaning there can be many CRED_DEF_PUBLIC related to the same SCHEMA while a CRED_DEF can only reference one single SCHEMA. Whenever an AnonCreds credential is issued, it is based on a CRED_DEF. That means, the issued credential can only have the attributes listed in the SCHEMA, which is referenced by the CRED_DEF_PUBLIC part. An exception to this is the blinded and signed link secret, an attribute which is part of every AnonCred credential. The issuer`s public keys for verifying the attributes (one key for one attribute) are within the CRED_DEF_PUBLIC, which allows validation of the credentials by verifiers.

The CRED_DEF_PRIVATE part of a CRED_DEF is stored on the issuer's side and is an object that contains the issuer`s private keys for signing the attributes (one key for one attribute) when issuing an AnonCreds Credential. These private keys are used to create signature proofs for the issued anonymous credentials, which then can be validated in a derived form by a verifier by using the published public keys of the CRED_DEF_PUBLIC part. A CRED_DEF_PRIVATE never leaves the Issuer's domain and is stored securely.

Revokable Verifiable Credentials require (besides) a CRED_DEF also a REV_REG_DEF.

Credential Offer

A credential offer is an offering from an issuer towards a holder to issue a credential. The credential offer contains the details about the claims the issuer intends to issue to the holder. A holder can reply to the issuer with a Credential Request. A credential offer also includes a nonce.

Credential Request

A credential request is a request from an holder towards a issuer to get a credential issued by the issuer. The credential request references a preceding Credential offer and defines the claims the holder wants to get issued. A credential request also includes a nonce.

DID

A Decentralized Identifier (DID), defined by the W3C DID Core Specification, is a type of identifier that enables verifiable, decentralized digital identity. A DID refers to any subject (e.g., a person, organization, thing, data model, abstract entity, etc.) as determined by the controller of the DID. DIDs are not used in AnonCreds itself but there must be an DID-based, enforced relationship between the schema and issuers and the AnonCreds objects they publish. This is outlined in a note in this section of this specification.

holder

A holder, also known as an identity holder, is an entity that is in possession of a credential. In many use cases, the holder is also the identity subject. A holder can interact with an issuer to obtain anonymous credentials. It can also derive information from anonymous credentials that can be presented to a verifier to gain access to goods and services.

issuer

An issuer is one of the three entities that interact with each other within the domain of digital identities. It can assert claims about a subject in the form of a tamper-proof credential whose origins are cryptographically verifiable.

link secret

One of the most significant differences between the AnonCreds and W3C Verifiable Credentials is how a credential is bound to the holder. With the Verifiable Credential, the holder binding happens without additional interactions between the holder and issuer. However, this approach comes with a lack of privacy for the holder. The correlatability of credentials due to the necessity of revealing a persistent identifier related to the holder is one such privacy issue.

AnonCreds are bound to the holder with a non-correlatable secret only known to the holder itself called a link secret*. Instead of a persistent identifier, the link secret as a blind attribute is sent to the issuer during credential issuance. The issuer signs every claim (including the blinded link secret) individually, enabling selective disclosure (see below). It means the issuer does not know the exact value of the link secret, and the holder can prove the ownership of credentials to a verifier without disclosing a persistent identifier.

*) The link secret is known as master secret in the Hyperledger Indy source code. However, the term “master secret” is outside the source code and older publications deprecated.

nonce

A nonce is an arbitrary unique number that is used to ensure secure communications. Within AnonCreds, nonces are used during credential issuance e.g. for binding a Credential Request to a Credential Offer.

predicates

A predicate is a boolean assertion about the value of a claim without disclosing the value itself. In contrast to any signature suite and algorithm implemented according to the W3C Verifiable Credentials, predicates are fully supported by AnonCreds.

Non-Revocation Proof (NRP)

TODO

REV_REG_DEF

A REV_REG_DEF object (short for “revocation registry definition”) contains information required for verifiers in order to enable them to verify whether a revokable verifiable credential has been revoked by the issuer since issuance.

REV_REG_DEFs are only needed for revokable verifiable credentials and are most commonly written to a public location (e.g. an indy ledger) by the owner of a CRED_DEF immediatly after the CRED_DEF has been written. They can be read from a Hyperledger Indy Node by any client and are updated in case of the revocation of a credential, which is based on the used CRED_DEF.

Further details about Hyperledger Indy’s revocation process can be found here.

REV_REG_ENTRY

A REV_REG_ENTRY object (short for “revocation registry entry”) marks the current status (“revoked” or “not revoked”) of one or more revokable verifiable credentials in the ledger in a privacy preserving manner. A REV_REG_ENTRY is written by the owner of a REV_REG_DEF respectively the issuer of the credential(s) based on a CRED_DEF and its REV_REG_DEF. Any REV_REG_ENTRY condensed with further required information can be read by any Hyperledger Indy client.

Further details about Hyperledger Indy’s revocation process can be found here.

SCHEMA

A SCHEMA object is a template that defines a set of attributes (also known as attribute names or claims) which are going to be used by issuers for issuance of Verifiable Credentials within a Hyperledger Indy network. SCHEMAs have a name, version and can be written to the ledger by any entity with proper permissions. SCHEMAs can be read from a Hyperledger Indy Node by any client.

In Hyperledger Indy, Credentials are based on a CRED_DEF. Therefore CRED_DEFs reference a Schema in order to define which attribute(names) will be used within the CRED_DEF.

SCHEMA publisher

A SCHEMA publisher is an entity that creates a SCHEMA to the ledger. It can be the issuer, but it can also be another entity that creates a SCHEMA that can be used by many issuers to create CRED_DEFs (see below).

selective disclosure

Selective disclosure is the ability to disclose partial information from an issued credential by disclosing only a subset of claims.

subject

A subject, also known as an identity subject, is the entity about whom claims are made.

Verifiable Data Registry

DIDs and DID documents have to be stored on some kind of system, which is available (to the public, in most cases). Such a system can be a distributed ledger, a (decentralized) file system, database and others. Such an anchor for [[ref: DID]s] and DID documents is called Verifiable Data Registry.

In the case of Hyperledger Indy a distributed ledger is used as Verifiable Data Registry. Besides DIDs and DID documents an instance of a Hyperledger Indy network stores additional data on the ledger, which is required for issuance (e.g. SCHEMA and CRED_DEF), verification (e.g. REV_REG_DEF)) and revocation (e.g REV_REG_ENTRY) of credentials.

Verifier

A verifier is an entity that validates identity information from a holder to grant access to goods and services.

Witness Delta

The witness delta is an update by the issuer of the list of revoked credentials at the time an updated accumulator is published with a REV_REG_ENTRY. The delta tells holders generating a Non-Revocation Proof (NRP) how to adjust their witness (referencing other indexes in the public tails file) to bring it back into harmony with the current value of the accumulator, such that the updated witness times the private factor of the credential once again equals the accumulator value.

zero-knowledge proofs

In cryptography, the zero-knowledge proof is a method by which an entity can prove that they know a certain value without disclosing the value itself. Zero-knowledge proofs can enable holders to:

  • Combine multiple credentials into a single proof to present to a verifier without revealing any correlatable identifier.
  • use predicates (see below) for enclosing logical expressions, such as the holder being older than 18 without disclosing the value.

AnonCreds are capable of all three features mentioned above.

NOTE

Question: Should the items that are AnonCreds data models be included in this?

§ AnonCreds Setup Data Flow

The following sequence diagram summarizes the setup operations performed by a SCHEMA Publisher, the Issuer (one required and one optional) in preparing to issue an AnonCred credential based on provided SCHEMA, and the one setup operation performed by each Holder. On successfully completing the operations, the Issuer is able to issue credentials based on the given SCHEMA to the Holder. The subsections below the diagram detail each of these operations.

sequenceDiagram autonumber participant L as Verifiable
Data Registry participant SP as Schema Publisher participant I as Issuer participant H as Holder Note over L, H: Schema Publisher: Publish Schema SP ->> L: Publish Schema (Schema) L ->> I: Schema ID,
Schema Transaction ID Note over L, H: Issuer: Create, Store and Publish CredDef I ->> I: create_and_store_credential_def
(Schema, tag, support_revocation) Note right of I: store public /
private keys and
correctness proof I ->> L: Publish CredDef (CredDef) Note over L, H: Issuer: Create, Store and Publish Revocation Registry (Optional) I ->> I: create_and_store_revoc_reg (intCredDef) Note right of I: get keys Note right of I: store revoc_reg_def,
revoc_reg_accum,
priv_key,
tails_generator I ->> L: Publish RevReg
(revoc_reg_id,
revoc_reg_def_json,
revoc_reg_entry_json) Note over L, H: Holder: Create and Store Link Secret H ->> H: indy_prover_create_master_secret H ->> H: store master secret rect rgb(191, 223, 255) Note left of H: 💡The "Verifier" role is
omitted in this
diagram, since
it is not required
for the setup end
NOTE

Those with a knowledge of DIDs might expect that in the flow above, the first step would be for the issuer to publish a DID. However, in AnonCreds, DIDs are not used in the processing of credentials, and notably, the public keys used in AnonCreds signatures come not from DIDs, but rather from CRED_DEF objects. DIDs may be used to identify the entity publishing the objects that are then used in the processing of credentials – the SCEHMA, CRED_DEF, REV_REG_DEF and REV_REG_ENTRY objects. There is an enforced relationship between an identifier (such as a DID) for the entity publishing the AnonCred objects, and the objects themselves. For example, in the Hyperledger Indy implementation of AnonCreds, for a credential issuer to publish a CRED_DEF on an instance of Indy it must have a DID on that instance, and it must use that DID to sign the transaction to write the CRED_DEF.

The DID of the publisher of an AnonCreds object MUST be identifiable from the published object and enforcement of the relationship between the DID and the object must be enforced. For example, in the Hyperledger Indy implementation of AnonCreds, the DID of the object publisher is part of the identifier of the object – given the identifier for the AnonCreds object (e.g. one found in proving a verifiable credential), the DID of the publisher can be found. Further, the Hyperledger Indy ledger enforces, and makes available for verification, the requirement that the writing of the AnonCreds object must be signed by the DID that is writing the object.

If a DID-based messaging protocol, such as DIDComm is used between the AnonCreds participants (the issuer, holder and verifier) the use of DIDs for messaging is independent of their use (or not) in the publishing AnonCreds objects. Such DIDs are used to facilitate secure messaging between the participants to enable the issuing of credentials and the presentation of proofs.

§ SCHEMA Publisher: Publish SCHEMA Object

Each type of AnonCred credential is based on a SCHEMA published to a Verifiable Data Registry (VDR), an instance of Hyperledger Indy in this version of AnonCreds. The SCHEMA is defined and published by the SCHEMA Publisher. Any issuer who can reference the SCHEMA (including the SCHEMA Publisher) MAY issue credentials of that type by creating and publishing a CRED_DEF based on the SCHEMA. This part of the specification covers the operation to create and publish a SCHEMA. The flow of operations to publish a SCHEMA is illustrated in the SCHEMA Publisher: Publish SCHEMA section of the AnonCreds Setup Data Flow sequence diagram.

The SCHEMA is a JSON structure that can be manually constructed, containing the list of attributes (claims) that will be included in each AnonCreds credential of this type. The following is an example SCHEMA:

{
    "attr_names": [
        "birthlocation",
        "facephoto",
        "expiry_date",
        "citizenship",
        "name",
        "birthdate",
        "firstname",
        "uuid"
    ],
    "name": "BasicIdentity",
    "version": "1.0.0"
}

Once constructed, the SCHEMA is published to a Verifiable Data Registry (VDR) using the Schema Publishers selected AnonCreds Objects Method. The schemaId identifier for the schema is dependent on where the schema is published. For example, see this SCHEMA that is published on the Sovrin MainNet instance of Hyperledger Indy. The schemaId for that object is: Y6LRXGU3ZCpm7yzjVRSaGu:2:BasicIdentity:1.0.0

§ Issuer Create and Publish CRED_DEF Object

Each Issuer of credentials of a given type (e.g. based on a specific SCHEMA) must create a CRED_DEF for that credential type. The flow of operations to create and publish a CRED_DEF is illustrated in the Issuer: Create, Store and Publish CRED_DEF section of the AnonCreds Setup Data Flow sequence diagram.

In AnonCreds, the CRED_DEF and CRED_DEF identifier include the following elements.

We’ll initially cover the generation and data for a CRED_DEF created without the option of revoking credentials. In the succeeding section, we describe the additions to the generation process and data structures when credential revocation is enabled for a given CRED_DEF.

§ Retrieving the SCHEMA Object

Prior to creating a CRED_DEF, the Issuer must get an instance of the SCHEMA upon which the CRED_DEF will be created. If the Issuer is also the SCHEMA Publisher, they will already have the SCHEMA. If not, the Issuer must request that information from the VDR on which the SCHEMA is published. In some AnonCreds Objects there is a requirement that the SCHEMA and CRED_DEF must be on the same VDR.

§ Generating a CRED_DEF Without Revocation Support

The CRED_DEF is a JSON structure that is generated using cryptographic primitives (described below) given the following inputs.

The operation produces two objects, as follows.

The following describes the process for generating the CRED_DEF and PRIVATE_CRED_DEF data.

TODO

Describe the generation process for the CRED_DEF.

The PRIVATE_CRED_DEF produced by the generation process has the following format:


To Do.

The CRED_DEF has the following format (from this example CRED_DEF on the Sovrin MainNet):

{
  "data": {
    "primary": {
      "n": "779...397",
      "r": {
            "birthdate": "294...298",
            "birthlocation": "533...284",
            "citizenship": "894...102",
            "expiry_date": "650...011",
            "facephoto": "870...274",
            "firstname": "656...226",
            "master_secret": "521...922",
            "name": "410...200",
            "uuid": "226...757"
      },
      "rctxt": "774...977",
      "s": "750..893",
      "z": "632...005"
    }
  },
  "ref": 54177,
  "signature_type": "CL",
  "tag": "latest"
}

The CRED_DEF contains a cryptographic public key that can be used to verify CL-RSA signatures over a block of L messages m1,m2,...,mL. The CRED_DEF contains a public key fragment for each message being signed by signatures generated with the respective private key. The length of the block of messages, L, being signed is defined by referencing a specific Schema with a certain number of attributes, A = a1,a2,.. and setting L to A+1. The additional message being signed as part of a credential is for a master_secret (called the link_secret everywhere except in the existing open source code and data models) attribute which is included in all credentials. This value is blindly contributed to the credential during issuance and used to bind the issued credential to the entity to which it was issued.

All integers within the above CRED_DEF example json are shown with ellipses (e.g. 123...789). They are 2048-bit integers represented as 617 decimal digits. These integers belong to an RSA-2048 group characterised by the n defined in the CRED_DEF.

TODO

Evaluate the impact in the existing implementations of making the ref an identifier vs. the current Hyperledger Indy Txn number – an integer.

The credDefId identifier for the Cred_Def is dependent on the AnonCreds Objects Method used in publishing the SCHEMA.

§ Generating a CRED_DEF With Revocation Support

The issuer enables the ability to revoke credentials produced from a CRED_DEF by passing to the CRED_DEF generation process the flag support_revocation as true. When revocation is to enabled for a CRED_DEF, additional data related to revocation is generated and added to the CRED_DEF JSON objects defined above. In the following the additional steps in the CRED_DEF generation process to enable revocation are described, along with the additional data produced in that process.

The following describes the process for generating the revocation portion of the CRED_DEF data when the CRED_DEF is created with the support_revocation flag set to true. This process extends the process for generating a CRED_DEF in the previous section of this document.

TODO

Describe the revocation data generation process for the CRED_DEF. Provide a reference to the published articles on revocation used here.

A PRIVATE_CRED_DEF with revocation enabled has the following format. In this, the details of the primary element are hidden, as they are the same as was covered above.


To Do.

A CRED_DEF with revocation enabled has the following format (from this example CRED_DEF on the Sovrin MainNet). In this, the details of the primary element are hidden, as they are the same as was covered above.

{
  "data": {
    "primary": {...},
    "revocation": {
      "g": "1 154...813 1 11C...D0D 2 095..8A8",
      "g_dash": "1 1F0...000",
      "h": "1 131...8A8",
      "h0": "1 1AF...8A8",
      "h1": "1 242...8A8",
      "h2": "1 072...8A8",
      "h_cap": "1 196...000",
      "htilde": "1 1D5...8A8",
      "pk": "1 0E7...8A8",
      "u": "1 18E...000",
      "y": "1 068...000"
    }
  },
  "ref": 54753,
  "signature_type": "CL",
  "tag": "state_license"
}

The elements with ellipses (e.g. 1F0...000) in g are 64 digits hex integers. The rest of the elements are the same structure as g but containing either 3 or 6 hex integers, as noted below. In the following, only the revocation item is described, as the rest of items (primary, ref, etc.) are described in the previous section of this document.

§ Publishing the CRED_DEF on a Verifiable Data Registry

Once constructed, the CRED_DEF is published by the Issuer to a Verifiable Data Registry using the issuers preferred AnonCreds Objects. For example, see this CRED_DEF that is published in the Sovrin MainNet instance of Hyperledger Indy. The full contents of the CRED_DEF is placed in the ledger, including the revocation section if present.

§ Issuer Create and Publish Revocation Registry Objects

Once the issuer has created a CRED_DEF with revocation enabled, the issuer must also create and publish a REV_REG_DEF and create and publish the first REV_REG_ENTRY for the registry.

In this section, we’ll cover the create and publish steps for each of the REV_REG_DEF and REV_REG_ENTRY objects. The creation and publishing of the REV_REG_DEF includes creating and publishing the TAILS_FILE for the REG_REV.

§ Creating the Revocation Registry Object

A secure process must be run to create the revocation registry object, taking the following input parameters.

Three outputs are generated from the process to generate the REV_REG: the REV_REG object itself, the TAILS_FILE content, and the PRIVATE_REV_REG object.

§ Recommend Not Using ISSUANCE_ON_DEMAND
WARNING

Based on the experience of the AnonCreds community in the use of revocable credentials, it is highly recommended the ISSUANCE_ON_DEMAND approach NOT be used unless absolutely required by your use case.

The reason this approach is not recommended is that if the issuer creates the REV_REG with the issuanceType item set to ISSUANCE_ON_DEMAND, the issuer must publish a RevRegEntry (as described in the revocation section) as each credential is issued resulting in many RevRegEntry transactions being performed, one per credential issued. Of course, with either issueanceType, there must still be one transaction for each batch of (1 or more) credentials revoked.

Further, if a credential contains some kind of “Issue Date” attribute in the credential and it is shared with verifiers, those verifiers can use that value to find the RevRegEntry transaction that activated the credential at the same time to learn the index of the holder’s credential within the RevReg, giving the verifiers both a correlatable identifier (RevRegId+index) for the holder’s credential and a way to monitor if that credential is ever revoked in the future.

For these reasons we anticipate the deprecation or removal of the ISSUANCE_ON_DEMAND approach in the next version of AnonCreds specification. Feedback from the community on this would be appreciated. We are particularly interested in understanding what use cases there are for ISSUANCE_ON_DEMAND.

§ REV_REG_DEF Object Generation

The REV_REG_DEF object has the following data model. This example is from this transaction on the Sovrin MainNet and instance of Hyperledger Indy.

{
  "credDefId": "Gs6cQcvrtWoZKsbBhD3dQJ:3:CL:140384:mctc",
  "id": "Gs6cQcvrtWoZKsbBhD3dQJ:4:Gs6cQcvrtWoZKsbBhD3dQJ:3:CL:140384:mctc:CL_ACCUM:1-1024",
  "revocDefType": "CL_ACCUM",
  "tag": "1-1024",
  "value": {
    "issuanceType": "ISSUANCE_BY_DEFAULT",
    "maxCredNum": 1024,
    "publicKeys": {
      "accumKey": {
        "z": "1 0BB...386"
      }
    },
    "tailsHash": "BrCqQS487HcdLeihGwnk65nWwavKYfrhSrMaUpYGvouH",
    "tailsLocation": "https://api.portal.streetcred.id/agent/tails/BrCqQS487HcdLeihGwnk65nWwavKYfrhSrMaUpYGvouH"
  }
}

The items within the data model are as follows:

TODO

Update this to be the inputs for generating a REV_REG vs. the already published object

As noted, most of the items come directly from the input parameters provided by the issuer. The z REV_REG accumulator public key is generated using (TODO: fill in details) algorithm. The use of the accumulator public key is discussed in the Credential Issuance section, when the publication of revocations is described. The calculation of the tailsHash is described in the next section on TAILS_FILE generation.

§ Tails File and Tails File Generation

The second of the outcomes from creating of a REV_REG is a TAILS_FILE. The contents of a TAILS_FILE is an array of calculated prime integers, one for each credential in the registry. Thus, if the REV_REG has a capacity (maxCredNum) of 1000, the TAILS_FILE holds an array of 1000 primes. Each credential issued using the REV_REG is given its own index (1 to the capacity of the REV_REG) into the array, the index of the prime for that credential. The contents of the TAILS_FILE is needed by the issuer to publish the current state of revocations within the REV_REG and by the holder to produce (if possible) a “proof of non-revocation” to show their issued credential has not been revoked.

The process of generating the primes that populate the TAILS_FILE is as follows:

TODO

To Do: Document the process for generating the primes.

Once generated, the array of primes is static, regardless of credential issuance or revocation events. Once generated, the SHA256 (TO BE VERIFIED) hash of the array of primes is calculated and returned to be inserted into the tailsHash item of the REV_REG object (as described in the previous section). Typically, the array is streamed into a file (hence, the term “Tails File”) and published to a URL indicated by the tailsLocation input parameter provided by the issuer.

The format of a TAILS_FILE is as follows:

TODO

To Do: Define the format of the Tails File

While not required, the Hyperledger Indy community has created a component, the “Indy Tails Server,” which is basically a web server for tails files. Holders get the tailsLocation during the issuance process, download the TAILS_FILE (ideally) once and cache it for use when generating proofs of non-revocation when creating a presentation that uses its revocable verifiable credential. How the TAILS_FILE is used is covered elsewhere in this specification:

§ PRIVATE_REV_REG Object Generation

In addition to generating the REV_REG object, a PRIVATE_REV_REG object is generated and securely stored by the issuer. The data model and definition of the items in the PRIVATE_REV_REG is as follows:

TODO

To Do: Fill in the details about the PRIVATE_REV_REG

§ Publishing the Revocation Registry Object

Once constructed, the REV_REG is published by the issuer in a Verifiable Data Registry using the issuer’s AnonCreds Objects. For example, see this REV_REG that is published on the Sovrin MainNet instance of Hyperledger Indy. The binary TAILS_FILE associated with the REV_REG can be downloaded from the tailsLocation in the REV_REG object.

§ Creating the Initial Revocation Registry Entry Object

Published REV_REG_ENTRY objects contain the state of the REV_REG at a given point in time such that holders can generate a proof of non-revocation (or not) about their specific credential and verifiers can verify that proof. An initial REV_REG_ENTRY is generated and published immediately on creation of the REV_REG so that it can be used immediately by holders. Over time, additional REV_REG_ENTRY objects are generated and published as the revocation status of one or more credentials within the REV_REG change.

A secure process must be run to create the initial REV_REG_ENTRY object, taking the following input parameters.

The process collects from the identified PRIVATE_REV_REG information to calculate the cryptographic accumulator value for the initial REV_REG_ENTRY, including:

With the collected information, the process the initial cryptographic accumulator for the REV_REG. The format of the identifier for the REV_REG_ENTRY is dependent on the AnonCreds Objects Method used by the issuer.

In simple terms, the cryptographic accumulator at any given point in time is the (modulo) product of the primes for each non-revoked credential in the REV_REG. Based on the value of issuanceType, all of the credentials are initially either revoked, or unrevoked. If all of the credentials are initially revoked, the accumulator value is 0, if all are unrevoked, the accumulator value has contributions from all of the entries in the array of primes.

The accumulator is calculated using the following steps:

TODO

To Do: Adding the algorithm for calculating the accumulator

THe following is an example of an initial, published REV_REG_ENTRY object:

{
  "revocDefType": "CL_ACCUM",
  "revocRegDefId": "Gs6cQcvrtWoZKsbBhD3dQJ:4:Gs6cQcvrtWoZKsbBhD3dQJ:3:CL:140389:mctc:CL_ACCUM:1-1024",
  "value": {
    "accum": "21 10B...33D"
  }
}

The items in the data model are:

To see what REV_REG_ENTRY transactions look like on a VDR, this is a link to an initial REV_REG_ENTRY where the credentials are initially all revoked, while this is a link to an initial REV_REG_ENTRY where all of the credentials are unrevoked.

§ Publishing the Initial Initial Revocation Registry Entry Object

Once constructed, the initial REV_REG_ENTRY is published by the issuer in a Verifiable Data Registry using their selected AnonCreds Objects Method. For example, see this REV_REG_ENTRY that is published on the Sovrin MainNet instance of Hyperledger Indy.

To prepare to use AnonCreds credentials, the Holder must create a link secret, a unique identifier that allows credentials issued to a Holder to be bound to that Holder and presented without revealing a unique identifier, thus avoiding correlation of credentials by Verifiers. The link_secret is kept private by the Holder. The link secret is used during the credential issuance process to bind the credential to the holder and in the generation of a presentation. For the latter, it allows the holder to create a zero knowledge proof that they were issued the credential by demonstrating knowledge of the value of the link_secret without sharing it. The details of how the link_secret is used to do this is provided in the issuance, presentation generation and verification sections of this specification.

The link secret is a sufficiently random unique identifier. For example, in the Hyperledger Indy implementation, the link secret is produced by a call to the Rust uuid Crate’s new_v4() method to achieve sufficient randomness.

Once generated, the link_secret is stored locally by the Holder for use in subsequent issuance and presentation interactions. If lost, the Holder will not be able to generate a proof that the credential was issued to them. The holder generates only a single link_secret, using it for all credentials the holder is issued. This allows for verifiers to verify that all of the credentials used in generating a presentation with attributes from multiple credentials were all issued to the same Holder without requiring the Holder to disclose the unique identifier (link_secret) that binds these credentials together.

There is nothing to stop a Holder from generating multiple link_secrets and contributing them to different credential issuance processes. However, doing so prevents the Holder from producing a presentation combining credentials issued to distinct link_secrets that can be proven to have been issued to the same entity. It is up to the Verifier to require and enforce the binding between multiple credentials used in a presentation.

§ AnonCreds Issuance Data Flow

The issuance of an anonymous credential requires several steps and involves the roles issuer, holder as well as the Verifiable Data Registry (see diagramm below).

sequenceDiagram autonumber participant L as Verifiable
Data Registry participant I as Issuer participant H as Holder I ->> I: Create Credential Offer I ->> H: Send Credential Offer H ->> H: Process Credential Offer opt H ->> L: Request SCHEMA L ->> H: Return SCHEMA end H ->> L: Request CRED_DEF L ->> H: Return CRED_DEF H ->> H: Create Credential Request H ->> I: Send Credential Request I ->> I: Process Credential Request I ->> I: Issue Credential I ->> H: Send Credential H ->> H: Store Credential rect rgb(191, 223, 255) Note left of H: 💡The "Verifier" and "Schema Publisher" roles are
omitted in this diagram, since they is not required
for the credential issuance data flow. end

The issuer prepares a Credential Offer for the holder (step 1). A Credential Offer includes information about what kind of credential (based on which CRED_DEF) the issuer is intending to issue to the holder. The issuer sends the Credential Offer to the holder (step 2), who then evaluates the incoming offer (step 3) and subsequently fetches required data (the CRED_DEF) from the Verifiable Data Registry (step 4-7).

Based on the CRED_DEF received from the Verfiable Data Registry, the holder prepares a Credential Request (step 8). A Credential Request is a formal request from a holder to an issuer to get a credential based on the given CRED_DEF issued to the holder. The holder sends the Credential Request to the issuer (step 9), who then evaluates the incoming request (step 10).

The issuer can decide whether to accept the received Credential Request and issues the credential (step 11) in the case of request acceptance. The issuer sends the credential to the holder (step 12), who then can store the received credential in his wallet (step 13).

§ Credential Offer

Before issuing a credential to the holder, the issuer has to send a Credential Offer to the potential holder (step 1 and 2). A Credential Offer contains information about the credential the issuer intends to issue and send to the holder. For creating a Credential Offer, the issuer is required to fetch the CRED_DEF as well as its correctness proof from the Verifiable Data Registry. The issuer also prepares a nonce which will be embedded within the Credential Offer in order to prevent replay attacks and authenticate between protocol steps.

The resulting JSON for a created Credential Offer is shown here:

{
    "schema_id": string,
    "cred_def_id": string,
    // Fields below can depend on Cred Def type
    "nonce": string,
    "key_correctness_proof" : <key_correctness_proof>
}

The issuer sends the Credential Offer to the holder (step 2), who then can process the Credential Offer (step 3). In order to figure out, which kind of credential (which CRED_DEF and attributes) are offered to the holder, the holder needs to fetch the underlying SCHEMA from the Verifiable Data Registry by using the schema_id as provided in the received Credential Offer, since the referenced SCHEMA contains all attribute names of the offered credential (step 4 + 5).

In case the credential respectively its attributes is of interest for the holder, the holder can reply to the issuer`s Credential Offer with a Credential Request in order to ask the issuer for issuance of the offered credential and its attributes.

TODO
  • Add info to key_correctness_proof

§ Credential Request

A Credential Request is a formal request from a holder to an issuer to get a credential based on a concrete CRED_DEF issued by the issuer to the holder.

In order to be able as a holder to express within a Credential Request to the issuer which kind of credential the issuer shall issue to the holder, the holder requires the CRED_DEF from the Verifiable Data Registry if not already available in local storage (step 6 + 7). The Credential Request has to reference the same CRED_DEF and nonce as given in the preceding Credential Offer. Besides the CRED_DEF, the holder also requires his link secret in a blinded form, as well as the corresponding Correctness Proof of his link secret. The holder has now all relevant data for creating the Credential Request (step 8).

TODO
  • Add here: How does the link secret get blinded? How does the cryptography work? How does it work with correctness proof?

The resulting JSON for a created Credential Request is shown here:

{
  "prover_did" : string,
  "cred_def_id" : string,
  // Fields below can depend on Cred Def type
  "blinded_ms" : string,
  "blinded_ms_correctness_proof" : string,
  "nonce": string
}
TODO

is the prover_did the peer DID of the holder?

The issuer sends the Credential Request to the issuer (step 9), who then can reply to the holder by sending an issued credential.

§ Issue Credential

After the issuer received the Credential Request from the holder, the issuer processes the Credential Request and decides whether to issue the credential as requested in the Credential Request to the holder.

In case the issuer decides to issue the requested credential to the holder, the following steps have to be executed by the issuer:

TODO
  • check nonce?
  • check link secret and correctness proof?
  1. The issuer has to fetch the CRED_DEF for the cred_def_id given in the received Credential Request either from the ledger or local storage (if already available).
  2. Every raw attribute value for each attribute in the fetched CRED_DEF (respectively its Schema), which the issuer intends to issue to the holder, needs to be set.
  3. Every raw attribute value, which cannot successfully be parsed into an integer (e.g. “Alice”), must be encoded as integer. The same rule applies to the blinded link secret.
  4. Every raw attribute value, which can successfully be parsed into an integer (e.g. “2015”), shall not be encoded explicitely. In this case it is required to use the raw (integer) value also as the encoded one. The intermediate result of raw and encoded credential attribute values is as follows (cred_values_json):
{
    "first_name": {"raw": "Alice", "encoded": "1139481716457488690172217916278103335"},
    "last_name": {"raw": "Garcia", "encoded": "5321642780241790123587902456789123452"},
    "degree": {"raw": "Bachelor of Science, Marketing", "encoded": "12434523576212321"},
    "status": {"raw": "graduated", "encoded": "2213454313412354"},
    "ssn": {"raw": "123-45-6789", "encoded": "3124141231422543541"},
    "year": {"raw": "2015", "encoded": "2015"},
    "average": {"raw": "5", "encoded": "5"},
}
  1. The issuer has to fetch the holder`s blinded link secret from the received Credential Request. The blinded link secret is available in the received Credential Request at blinded_ms.
  2. The issuer has to sign each attribute value and the the blinded link secret by using the corresponding private key for each attribute as defined in the private part of the CRED_DEF earlier.
TODO
  • check how exactly the signing happens for the whole credential

The issuer has to transmit the whole credential data to the holder as follows:

{
    "schema_id": string,
    "cred_def_id": string,
    "rev_reg_id": null,
    "values": {
        "first_name": {
            "raw": "Alice",
            "encoded": "1139481716457488690172217916278103335"
        },
        "last_name": {
            "raw": "Garcia",
            "encoded": "5321642780241790123587902456789123452"
        },
        "degree": {
            "raw": "Bachelor of Science, Marketing",
            "encoded": "12434523576212321"
        },
        "status": {
            "raw": "graduated",
            "encoded": "2213454313412354"
        },
        "ssn": {
            "raw": "123-45-6789",
            "encoded": "3124141231422543541"
        },
        "year": {
            "raw": "2015",
            "encoded": "2015"
        },
        "average": {
            "raw": "5",
            "encoded": "5"
        }
    },
    "signature": {
        "p_credential": {
            "m_2": "99219524012997799443220800218760023447537107640621419137185629243278403921312",
            "a": "548556525746779881166502363060885...94013062295153997068252",
            "e": "25934472305506205990702549148069757193...84639129199",
            "v": "97742322561796582616103087460253...25643543159082080893049915977209167597"
        },
        "r_credential": null
    },
    "signature_correctness_proof": {
        "se": "898650024692810554511924969312...143339518371824496555067302935",
        "c": "93582993140981799598406702841334282100000866001274710165299804498679784215598"
    },
    "rev_reg": null,
    "witness": null
}
TODO
  • what is the naming scheme for the CL signatures in p_credential? Since the shown JSON is the result of two mixed examples, the signatures for more than the three presented attributes are missung. m_2 is the link secret…
TODO
  • Add description for remaining keys of json shown above
  • What kind of encoding algorithm for strings is used? Seems like this is not defined explicitely (https://jira.hyperledger.org/browse/IS-786)
  • Go deeper into signing with CL?
  • Encoding the raw blinded link secret value and using it as encoded one is correct?
  • consider revocation data in case of revocation

After the issuer sent the credential data to the holder (step 12), the holder can accept and store the credential within his wallet (step 13). The credential issuance flow is completed at this point.

§ AnonCreds Presentation Data Flow

sequenceDiagram autonumber participant L as Verifiable
Data Registry participant SP as Schema Publisher participant I as Issuer participant H as Holder participant V as Verifier Note over L, V: AnonCreds Presentation Data Flow V->>V: Create presentation request V->>H: Send presentation request H->>L: Request schemas, credential definitions, public keys, revocation lists L->>H: Return schemas, credential definitions, public keys, revocation lists H->>H: Generate presentation H->>V: Send presentation V->>L: Request schemas, credential definitions, public keys, revocation lists L->>V: Return schemas, credential definitions, public keys, revocation lists V->>V: Verify presentation

The flow of operations to request, create, and verify a verifiable presentation is illustrated in the AnonCreds Presentation Data Flow sequence diagram.

The Verifier starts the process in step 1 by creating and sending a presentation request to the Holder.

In step 2, the Verifier sends the presentation request to the Holder.

In steps 3, 4, and 5, the Holder collects the required information and creates the verifiable presentation according to the presentation request received from the Verifier.

In step 6, the Holder sends the verifiable presentation according to the presentation request to the Verifier.

In step 7, 8, and 9, the Verifier collects the required information and verifies the verifiable presentation and accepts it if the signature is valid, otherwise rejects the verifiable presentation.

TODO

Question: VDR access for schema, revocation etc. retrieval mandatory?

§ Create Presentation Request

The Verifier starts the process in step 1 of the AnonCreds Presentation Data Flow by creating and sending a presentation request to the Holder.

The presentation request provides information about the required attributes and predicates to be revealed by the Holder as well as information about the credential revocation status and other restrictions.

The presentation request is generated by the Verifier in JSON format:

{
    "name": string,
    "version": string,
    "nonce": string, 
    "requested_attributes": { 
        "<attr_referent>": <attr_info>, 
        ...,
    },
    "requested_predicates": { 
        "<predicate_referent>": <predicate_info>, 
        ...,
     },
    "non_revoked": Optional<non_revoc_interval>, 
    "ver": Optional<str>
}

attr_info has the following format:

{
    "name": Optional<string>,
    "names": Optional<[string, string]>,
    "restrictions": Optional<wql query>,
    "non_revoked": Optional<non_revoc_interval>,
}

predicate_info has the following format:

{
    "name": string, 
    "p_type": string, 
    "p_value": int, 
    "restrictions": Optional<wql query>, 
    "non_revoked": Optional<non_revoc_interval>, 
}

§ Request Non-Revocation Proofs

The presentation request JSON item non_revoc_interval allows the verifier to define an acceptable non-revocation interval for a requested attribute(s) / predicate(s) with the following timestamps:

{
    "from": Optional<int>, 
    "to": Optional<int>, 
}

As noted in the examples above, the non-revoked item be may at the outer level of the presentation request such that it applies to all attributes and predicates, or at the attribute/predicate level, applying only to specific attributes and/or predicates.

The use of a “non-revoke interval” is intended to have the semantic meaning that the verifier will accept a non-revocation Proof (NRP) from any point in the from to to interval. The intention is that by being as flexible as the business rules allow, the holder and/or verifier may have cached VDR revocation data such that they don’t have to go to the VDR to get additional RevRegEntry data. In practice, the use of an interval here is not well understood and tends to cause confusion amongst presentation request developers. The AnonCreds community recommends always using matching to and from values (see Aries RFC 0441 Present Proof Best Practices).

While one would expect the to value to be the current time (“Prove the credential is not revoked right now”), its inclusion allows the verifier to ask for a NRP sometime in the past. This addresses use cases such as “Prove that your car insurance policy was not revoked on June 12, 2021 when the accident occurred.”

§ WQL Query

The restrictions on each attribute/predicate defines a query filter in Wallet Query Language (WQL) that is used to search the holder wallet (storage) for candidate credentials that meet the restrictions. WQL is not required in an AnonCreds implementation, but the function it performs is required. That is, the holder must scan the credentials it holds to find the ones that satisfy the requirements defined in the presentation request.

The WQL specification can be found in indy-sdk/docs/design/011-wallet-query-language/README.md

Wallets can be searched and filtered using a simple, JSON-based query language. WQL is inspired by MongoDB’s query syntax, and can be mapped to SQL, GraphQL, and other query languages supported by storage backends.

The list of allowed keys that can be combined into complex queries includes:

    "schema_id": <credential schema id>,
    "schema_issuer_did": <credential schema issuer did>,
    "schema_name": <credential schema name>,
    "schema_version": <credential schema version>,
    "issuer_did": <credential issuer did>,
    "cred_def_id": <credential definition id>,
    "rev_reg_id": <credential revocation registry id>, // "None" as string if not present
    // the following keys can be used for every `attribute name` in credential.
    "attr::<attribute name>::marker": "1", - to filter based on existence of a specific attribute
    "attr::<attribute name>::value": <attribute raw value>, - to filter based on value of a specific attribute

§ WQL Query Examples

  1. Get all credentials where subject like Acme% and issue_date > specified data. (Note: the name of the issue_date tag begins with a tilde, telling the wallet to store its value unencrypted, which makes the $gt operator possible.)

    "restrictions": {
        "~subject": {"$like": "Acme%"},
        "~issue_date": {"$gt": 2022-04-26}
    }
    
  2. Get all credentials where schema_id is contained in (a, b, c) and issuer_id in (d, e, f).

    "restrictions": {
        "schema_id": {
            "$in": ["a", "b", "c"]
        },
        "issuer_id": {
            "$in": ["d", "e", "f"]
        }
    }
    
  3. Get all credentials where

    1. cred_def_id is “NcYxi…TAG_1” AND issuer_did is “NcYxiDXkpYi6ov5FcYDi1e” OR
    2. cred_def_id is “CnEDk…TAG_1” AND issuer_did is “CnEDk9HrMnmiHXEV1WFgbVCRteYnPqsJwrTdcZaNhFVW”.
    "restrictions": [
        {
            "cred_def_id": "NcYxiDXkpYi6ov5FcYDi1e:3:CL:NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0:TAG_1",
            "issuer_did": "NcYxiDXkpYi6ov5FcYDi1e"
        },
        {
            "cred_def_id": "CnEDk9HrMnmiHXEV1WFgbVCRteYnPqsJwrTdcZaNhFVW:3:CL:CnEDk9HrMnmiHXEV1WFgbVCRteYnPqsJwrTdcZaNhFVW:2:gvt:1.0:TAG_1",
            "issuer_did": "CnEDk9HrMnmiHXEV1WFgbVCRteYnPqsJwrTdcZaNhFVW"
        }
    ]
    

§ Example of a complete presentation request

{   
    "nonce": "verifierNonce",
    "name": "pres_req_1",
    "version": "0.1",
    "requested_attributes": {
        "attr1_referent": {
            "name": "name"
        },
        "attr2_referent": {
            "name": "sex"
        },
        "attr3_referent": {
            "name": "phone"
        },
        "attr4_referent": {
            "names": ["name", "height"],
            "restrictions":
            {
                "cred_def_id": "NcYxiDXkpYi6ov5FcYDi1e:3:CL:NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0:TAG_1",
                "issuer_did": "NcYxiDXkpYi6ov5FcYDi1e"
            },
        }
    },
    "requested_predicates": {
        "pred1_referent": {
            "name": "age",
            "p_type": ">=",
            "p_value": 18,
            "restrictions": [{
                "issuer_did": "did",
                "schema_id": "id"
            }]
        }
    },
    "non_revoked": {
        "from": 1650876280,
        "to": 1682405051
    }
}

In step 2 of the AnonCreds Presentation Data Flow, the Verifier sends the presentation request to the Holder.

§ Generate Presentation

In step 3, 4, and 5 of the AnonCreds Presentation Data Flow, the Holder collects the required information and creates the verifiable presentation according to the presentation request received from the Verifier.

Either a corresponding credential with optionally revealed attributes or a self-attested attribute must be provided for each requested attribute. A presentation request may request multiple credentials from different schemas and multiple issuers, which should reside in the Holder’s wallet.

The verifier may specify in the presentation request that some or all of the attributes/predicates that are derived from revocable verifiable credentials held by the holder have an accompanying non-revocation proof (NRP). The generation of an NRP is described in this section of the specification.

NOTE

Often in discussions about verifiable presentations, the term “prover” is used to indicate the participant generating the presentation. Throughout the Hyperledger Indy AnonCreds implementation the term prover is used in the names of methods performed by that participant. However, because in AnonCreds the holder and the prover are always the same entity, we’ll use holder to refer to the participant generating the requested presentation to emphasize that the same entity is both issued credentials and generating presentations from those credentials.

§ Generate AnonCreds Presentation

Before the Holder can generate the proof, he needs to collect all required credentials from the Holder wallet based on the provided presentation request. Instead of immediately returning fetched credentials, a three-step procedure is used, first creating a search_handle, then fetching credentials in batches, and finally closing the request.

The holder then needs to create a requested_credentials_json document indicating the attributes and predicates to reveal.

Finally, all required schemas, required public keys and revocation registries must be provided, typically by querying the verifiable data registry (VDR).

Once all required information is available, the Holder generates the presentation.

  1. indy_prover_search_credentials_for_proof_req: This API call returns a search_handle that can be used to fetch records by small batches (with indy_prover_fetch_credentials_for_proof_req).

    pub extern fn indy_prover_search_credentials_for_proof_req(command_handle: CommandHandle,
                                                              wallet_handle: WalletHandle,
                                                              proof_request_json: *const c_char,
                                                              extra_query_json: *const c_char,
                                                              cb: Option<extern fn(
                                                                  command_handle_: CommandHandle, err: ErrorCode,
                                                                  search_handle: SearchHandle)>) -> ErrorCode {  
    
    • wallet_handle: Wallet handle (created by open_wallet).

    • proof_request_json: Proof request in JSON format.

    • extra_query_json: (Optional) list of extra queries that will be applied to the correspondent attribute/predicate <attr_referent> / <predicate_referent>, see wql_query.

      • Example:

        {
            "attr1_referent": {
                "attr::age::value": "28"
            }
        }
        
    • cb: Callback that takes command result as parameter.

    • Returns

      • search_handle: Search handle that can be used later to fetch records by small batches (with indy_prover_fetch_credentials_for_proof_req).
  2. indy_prover_fetch_credentials_for_proof_req: This API call fetches the next batch of credentials of size count for the requested item using proof request search_handle (created by indy_prover_search_credentials_for_proof_req).

    pub  extern fn indy_prover_fetch_credentials_for_proof_req(command_handle: CommandHandle,
                                                           search_handle: SearchHandle,
                                                           item_referent: *const c_char,
                                                           count: usize,
                                                           cb: Option<extern fn(command_handle_: CommandHandle, err: ErrorCode,
                                                                                credentials_json: *const c_char)>) -> ErrorCode {} 
    
    • search_handle: Search handle (created by indy_prover_search_credentials_for_proof_req).
    • item_referent: Referent of attribute/predicate in the proof request.
    • count: Count of credentials to fetch.
    • cb: Callback that takes command result as parameter.
    • Returns
      • credentials_json: List of credentials for the given proof request.

        [{
             "cred_info": <credential_info>,
             "interval": Optional<non_revoc_interval>
        }]
        

        where

        • credential_info:

          {
              "referent": string, - id of credential in the wallet
              "attrs": {"key1":"raw_value1", "key2":"raw_value2"}, - credential attributes
              "schema_id": string, - identifier of schema
              "cred_def_id": string, - identifier of credential definition
              "rev_reg_id": Optional<string>, - identifier of revocation registry definition
              "cred_rev_id": Optional<string> - identifier of credential in the revocation registry definition
          }
          
        • non_revoc_interval:

          {
              "from": Optional<int>, - timestamp of interval beginning
              "to": Optional<int>, - timestamp of interval ending
          }
          

        NOTE: If the length of the list is less than the requested count, then the search iterator correspondent to the requested item_referent is completed.

  3. indy_prover_close_credentials_search_for_proof_req: This API closes the credentials search for the proof request (invalidate search_handle)

    pub  extern fn indy_prover_close_credentials_search_for_proof_req(command_handle: CommandHandle,
                                                                      search_handle: SearchHandle,
                                                                      cb: Option<extern fn(command_handle_: CommandHandle, err: ErrorCode)>) -> ErrorCode {
    
    • search_handle: Search handle (created by indy_prover_search_credentials_for_proof_req).
  4. requested_credentials_json: The Holder defines how to reveal attributes and predicates. Either a credential (cred_id) or a self-attested attribute for each requested attribute and predicate is provided in JSON format:

    {
       "self_attested_attributes": {
           "self_attested_attribute_referent": string
       },
       "requested_attributes": {
           "requested_attribute_referent_1": {
              "cred_id": string, 
              "timestamp": Optional<number>, 
              "revealed": <bool> 
           },
           "requested_attribute_referent_2": {
              "cred_id": string, 
              "timestamp": Optional<number>, 
              "revealed": <bool> 
           }
       },
       "requested_predicates": {
           "requested_predicates_referent_1": {
               "cred_id": string,
               "timestamp": Optional<number>
           }
       }
    }
    

    Example:

    {
        "self_attested_attributes": {
            "attr1_referent": "Alice",
            "attr2_referent": "Garcia"
        },
        "requested_attributes": {
            "attr3_referent": {
                "cred_id": "123",
                "revealed": true
            },
            "attr4_referent": {
                "cred_id": "456",
                "revealed": true
            }
        },
        "requested_predicates": {
            "predicate1_referent": {
              "cred_id": "680"
            }
          }
    }
    
  5. indy_prover_create_proof: This API creates a presentation according to the presentation request

    • Either a corresponding credential with optionally revealed attributes or a self-attested attribute must be provided for each requested attribute (see indy_prover_get_credentials_for_pool_req).
    • A proof request may request multiple credentials from different schemas and different issuers.
    • All required schemas, public keys and revocation registries must be provided.
    • The proof request also contains nonce.
    • The proof contains either proof or self-attested attribute value for each requested attribute.
    pub extern fn indy_prover_create_proof(command_handle: CommandHandle,
                                           wallet_handle: WalletHandle,
                                           proof_request_json: *const c_char,
                                           requested_credentials_json: *const c_char,
                                           master_secret_id: *const c_char,
                                           schemas_json: *const c_char,
                                           credential_defs_json: *const c_char,
                                           rev_states_json: *const c_char,
                                           cb: Option<extern fn(command_handle_: CommandHandle, err: ErrorCode,
                                                                proof_json: *const c_char)>) -> ErrorCode {
    
    • wallet_handle: Wallet handle (created by open_wallet).

    • proof_request_json: Proof request in JSON format.

    • requested_credentials_json: Document specifying either a credential or self-attested attribute for each requested attribute in JSON format.

    • master_secret_id: The id of the master secret stored in the wallet.

      • Notes:
        • A Master Secret is an item of Private Data used by a Holder to guarantee that a credential uniquely applies to them.
        • The Master Secret is an input that combines data from multiple Credentials to prove that the Credentials have a common subject (the Holder).
    • schemas_json: Collection of all schemas participating in the proof request.

      {
          "schema1_id": <schema1>,
          "schema2_id": <schema2>,
          "schema3_id": <schema3>,
      }
      
    • credential_defs_json: Collection of all credential definitions participating in the proof request.

      {
          "cred_def1_id": <credential_def1>,
          "cred_def2_id": <credential_def2>,
          "cred_def3_id": <credential_def3>,
      }
      
    • rev_states_json: Collection all revocation states participating in the proof request.

      {
          "rev_reg_def1_id or credential_1_id": {
              "timestamp1": <rev_state1>,
              "timestamp2": <rev_state2>,
          },
          "rev_reg_def2_id or credential_1_id": {
              "timestamp3": <rev_state3>
          },
          "rev_reg_def3_id or credential_1_id": {
              "timestamp4": <rev_state4>
          },
      }
      

      Note: Use credential_id instead of rev_reg_id in case of proving several credentials from the same revocation registry.

    • cb: Callback that takes command result as parameter.

    • Returns

      • proof_json: Proof presentation for the given proof request.
        • For each requested attribute either a proof (with optionally revealed attribute value) or self-attested attribute value is provided.
        • Each proof is associated with a credential and corresponding schema_id, cred_def_id, rev_reg_id and timestamp.
        • There is also an aggregated proof part common for all credential proofs.

The resulting presentation proof_json created by the Holder has the following JSON format:

{
    "requested_proof": {
        "revealed_attrs": {
            "requested_attr1_id": {
                "sub_proof_index": number,
                "raw": string,
                "encoded": string
            },
            "requested_attr4_id": {
                "sub_proof_index": number,
                "raw": string,
                "encoded": string
            }
        },
        "revealed_attr_groups": {
            "requested_attr5_id": {
                "sub_proof_index": number,
                "values": {
                    "attribute_name": {
                        "raw": string,
                        "encoded": string
                    }
                }
            }
        },
        "unrevealed_attrs": {
            "requested_attr3_id": {
                "sub_proof_index": number
            }
        },
        "self_attested_attrs": {
            "requested_attr2_id": self_attested_value
        },
        "predicates": {
            "requested_predicate_1_referent": {
                "sub_proof_index": int
            },
            "requested_predicate_2_referent": {
                "sub_proof_index": int
            }  
        }
    }
    "proof": {
        "proofs": [
            <credential_proof>,
            <credential_proof>,
            <credential_proof>
        ],
        "aggregated_proof": <aggregated_proof>
    }
    "identifiers": [{schema_id, cred_def_id, Optional<rev_reg_id>, Optional<timestamp>}]
}

§ Example of a proof:
{
    "requested_proof": {
        "revealed_attrs": {
            "attr4_referent": {
                "sub_proof_index": 0,
                "raw": "graduated",
                "encoded": "2213454313412354"
            },
            "attr5_referent": {,
                "sub_proof_index": 0,
                "raw": "123-45-6789",
                "encoded": "3124141231422543541"
            },
            "attr3_referent": {
                "sub_proof_index": 0, 
                "raw": "Bachelor of Science, Marketing",
                "encoded": "12434523576212321"
            }
        },
        "self_attested_attrs": {
            "attr1_referent": "Alice",
            "attr2_referent": "Garcia",
            "attr6_referent": "123-45-6789"
        },
     "unrevealed_attrs": {
     },
     "predicates": {
          "predicate1_referent": {
              "sub_proof_index": 0
          }
     }
    "proof" : [] //# Validity Proof, to be checked by Verifier 
    "identifiers" : [ //# Identifiers of credentials that were used for Presentation building
        {
            "schema_id": "transcript_schema_id",
            "cred_def_id": "123",
            "rev_reg_id": "123_123",
            "timestamp": 1550503925
        },
        {
            "schema_id": "job_certificate_schema_id",
            "cred_def_id": "456",
            "rev_reg_id": "456_456",
            "timestamp": 1550503945
        }
    ]
}

§ Generate Non-Revocation Proofs

A holder preparing an AnonCreds presentation must determine what, if any, non-revocation proofs (NRPs) must be added to the presentation based on a combination of what is in the proof request, and what verifiable credentials are to be used in the presentation. As noted in the previous section, the presentation request may have the non-revoked item at the outer-most level, applying to all source credentials, or at the requested_attribute and/or requested_predicate level, applying only to specific source credentials. For each, the holder must also determine if the verifiable credential selected for attributes/predicates where a NRP is requested is a revocable credential. Obviously, a NRP cannot be produced for a verifiable credential issued without a RevReg.

Once the holder has determined the required NRPs needed for the presentation, they must generate a NRP for each applicable source verifiable credential and add the NRPs to the presentation. For each, the holder must collect the necessary data from the RevRegEntrys published by the issuer and then generate the NRP.

§ Collecting Data for Generating the Non-Revocation Proof

In order to produce a NRP, the holder must collect the following information from wherever the issuer has published the information. Note that the holder may have some or all of this information cached from data previously collected.

The collection of the last two items is difficult without extra support of the entity holding the published RevReg (e.g. the VDR/ledger). Since each RevRegEntry holds only the list of active and revoked credential revocation status changes since the previous RevRegEntry (the “deltas”), a holder must retrieve those lists from every RevRegEntry from RevReg creation to the RevRegEntry holding the accumulator the holder will use for generating the NRP. The issuer could have made many calls to publish RevRegEntry transactions, and the holder would have to make a request for each one, which is not practical (and perhaps not even possible). In the Hyperledger Indy implementation, a special call (get_revoc_reg_delta) is used to collect the necessary data from all the RevRegEntry transactions for a specified interval in a single request. In the most used version of the call, the interval in the request is from 0 (meaning from when the RevReg was created) to the current time. If the holder has called the routine previously with an earlier to value and cached the results, the holder MAY use the time of the cached result as the from, so that only the credentials with revocation status changes since that time are returned. The holder then adds the returned lists to the cached lists. If the verifier has requested a “back in time” NRP, the holder may use a to date to match the date of interest to the verifier. When executed, the transaction returns:

Once collected, the holder processes the issued and revoked lists to determine the credential status (revoked or not) of every credential in the RevReg. As well, the holder can at the point see if the credential for which the NRP is being generated has been revoked, and decide to continue with the process (producing an unverifiable “proof”) or to stop the process, perhaps with a notification to the verifier.

§ Non-Revocation Proof Generation Steps

Given the data collected by the holder to produce the NRP, the following calculations are performed.

A witness is calculated in the same way as the accumulator (as described here), except the tails file factor of the credential being proven as not revoked is not included in the calculation. All of the tails file entries from the other unrevoked credentials are included.

Once the witness (u), the accumulator from the ledger (e) and the value of the tails file entry for the credential of interest (b) are known, the NRP can be generated as follows:

TODO

To Do: Add more detail about the calculation of Cu and Cb in the following.

This is the zero knowledge non-revocation proof.

Each NRP is added alongside the credential to which the NRP is applied, to the presentation generated by the holder using this data model:

"non_revoc_proof": {
    "x_list": {
        "rho": "...",
        "r": "...",
        "r_prime": "...",
        "r_prime_prime": "...",
        "r_prime_prime_prime": "...",
        "o": "...",
        "o_prime": "...",
        "m": "...",
        "m_prime": "...",
        "t": "...",
        "t_prime": "...",
        "m2": "...",
        "s": "...",
        "c": "..."
    },
    "c_list": {
        "e": "...",
        "d": "...",
        "a": "...",
        "g": "...",
        "w": "...",
        "s": "...",
        "u": "..."
    }
}

The values in the data model are:

TODO

To Do: Enumerate each of the items in each NRP section of the presentation.

As well, in the presentation data model, added to the identifiers item, is the timestamp (Unix epoch format) of the RevRegEntry used to construct the NRP (see example below). The verifier needs the rev_reg_id and timestamp to get the correct accumulator to use in verifying the NRP.

"identifiers": [
    {
        "schema_id": "7BPMqYgYLQni258J8JPS8K:2:degree schema:46.58.87",
        "cred_def_id": "7BPMqYgYLQni258J8JPS8K:3:CL:70:faber.agent.degree_schema",
        "rev_reg_id": "7BPMqYgYLQni258J8JPS8K:4:7BPMqYgYLQni258J8JPS8K:3:CL:70:faber.agent.degree_schema:CL_ACCUM:61d5a381-30be-4120-9307-b150b49c203c",
        "timestamp": 1656269796
    }
]

In step 6 of the AnonCreds Presentation Data Flow, the holder sends the verifiable presentation, including any embedded NRPs, to the verifier.

Link: indy-anoncreds/docs/dev/anoncred.pdf

§ Verify Presentation

In step 7, 8, and 9 of the AnonCreds Presentation Data Flow, the Verifier collects the required information and verifies the verifiable presentation and accepts it if the signature is valid, otherwise rejects the verifiable presentation.

This section covers the overall verification process of the attributes, predicates and link secret. Following that is a section that specifies the process for verifying the non-revocation proofs (if any) in the presentation.

pub extern fn indy_verifier_verify_proof(command_handle: CommandHandle,
                                         proof_request_json: *const c_char,
                                         proof_json: *const c_char,
                                         schemas_json: *const c_char,
                                         credential_defs_json: *const c_char,
                                         rev_reg_defs_json: *const c_char,
                                         rev_regs_json: *const c_char,
                                         cb: Option<extern fn(command_handle_: CommandHandle, err: ErrorCode,
                                                              valid: bool)>) -> ErrorCode {}

§ Verify Non-Revocation Proof

If the presentation includes one or more Non-Revocation Proofs (NRPs) the verifier must also extract from the verifiable presentation the NRPs and process each proof. If any of the NRPs cannot be verified because one or more of the attributes/predicates came from a revoked credential, the overall status of the presentation is rejected – not verifiable. The following outlines the process for verifying an NRP.

The verifier begins by extracting from the section of the presentation for a given revocable credential the non_revoc_proof and identifiers data items. The verifier must retrieve (possibly from its cache, otherwise from the VDR) the published RevRegEntry given the rev_reg_id and timestamp values from the identifiers data item. The verifier extracts the accumulator item from the RevRegEntry retrieved. Note that the verifier does not need to collect the revocation status of all of the credentials in the registry, nor the contents of the tails file for the RevReg. Only the issuer and holder needs that data. During the verification process, the verifier does not learn the index of the holder's credential in the RevReg.

Once the verifier gets the data in the non_revoc_proof data item from the presentation for the NRP being processed, plus the accumulator from appropriate RevRegEntry, the following steps are carried out to verify the NRP.

TODO

To Do: Outline the NRP verification calculation.

TODO

To Do: Is there a separate process to bind the NRP to the credential?

The verification code MUST surface to the verifier if any part of the presentation, including any NRP(s), fail cryptographic verification. The verification code MAY surface additional detail about what part of the presentation failed, such as which NRP failed verification (if any).

The verifier SHOULD evaluate the presentation to make sure that the holder provided all requested NRPs. Notably, if any expected NRPs are not received in the presentation, the verifier SHOULD check to see if the given credential type is revocable. If not, it is acceptable that no NRP was received. However, if the credential used in the generation of the proof is revocable, and the holder did not provide the NRP, the verification code SHOULD surface to the verifier that the presentation failed cryptographic verification.

§ AnonCreds Revocation Data Flow

AnonCreds includes a mechanism that supports the revocation of verifiable credentials. This mechanism includes:

A fundamental goal of AnonCreds is to not provide a correlatable identifier for either a holder or a credential as part of generation and verification of an AnonCreds presentation. Applying that goal to revocation means that the revocation mechanism must support the holder proving a credential used in generating a presentation is not revoked without providing a correlatable identifier for that credential or the holder itself. As such, the AnonCreds revocation mechanism uses a Zero Knowledge Proof (ZKP) that allows the holder to prove a credential they hold is not revoked without revealing an identifier for their credential or the holder.

§ AnonCreds Issuer Setup With Revocation

The details of issuer setting up revokable credential types are covered in the issuer setup section of this specification. Note the warning and recommendation against the use of ISSUANCE_ON_DEMAND in that part of the specification.

§ AnonCreds Issuance with Revocation

The details of an issuer issuing a revokable credential to a holder are covered in the issuance data flow section of this specification.

§ AnonCreds Credential Activation/Revocation and Publication

When an issuer decides to revoke a previously issued credential (or activate a previously inactive/revoked credential), they do so by publishing another instance of the RevRegEntry object. Recall from the issuer setup section, the specification about RevRegEntry[creating and publishing the first RevRegEntry](data_flow_setup.md#creating-the-initial-revocation-registry-entry-object) for a RevReg. In that process, the accumulator for the initial state of the RevReg is published. When subsequent RevRegEntry transactions are published to the ledger, each includes an updated value of the accumulator. The update of the accumulator is necessary with each revocation or (re)activation of a credential or set of credentials since the last published RevRegEntry. This is because only the factors (all factors are listed in the respective tails file) of credentials which are active (meaning not being revoked) contribute to the accumulator. Therefore in addition to the updated accumulator value, every RevRegEntry contains lists of indices of credential factors which have been either revoked or (re)activated within the RevRegEntry. This list of factor indices is a so called Witness and enables the Holder to successfully generate a proof of non revocation.

An example of the data in the RevRegEntry is shown in the following example of a RevRegEntry, pulled from this transaction on the Sovrin MainNet.

"data": {
    "revocDefType": "CL_ACCUM",
    "revocRegDefId": "4xE68b6S5VRFrKMMG1U95M:4:4xE68b6S5VRFrKMMG1U95M:3:CL:59232:default:CL_ACCUM:4ae1cc6c-f6bd-486c-8057-88f2ce74e960",
    "value": {
        "accum": "21 116...567",
        "prevAccum": "21 128...C3B",
        "issued": [
        ],
        "revoked": [
            172
        ]
    }
},

In the above:

In the example transaction above no credentials are issued (meaning changed from status revoked to issued) and only one, the credential with index 172, is changed to revoked. Both lists can have an arbitrary number of entries, up to the total number of credentials in the RevReg.

The algorithm to calculate the value of a RevRegEntry accumulator at any time is the same: determine the (modulo) product of the primes for each non-revoked credential in the REV_REG, as described here.

NOTE

The issuer MUST track of the revocation status of all of the credentials within a RevReg so that it can both calculate the correct accumulator and send to the VDR accurate lists (issued and revoked) of the indices of the credentials whose status has changed since the last RevRegEntry was published. If the list and accumulator published to VDR get out of sync a holder will not be able to generate a valid NRP.

A VDR publishing a RevReg MAY perform its own calculation of the accumulator based on the list updates received in a RevRegEntry transaction to ensure that the calculation of the accumulator after all of the revocation status updates to the credentials within the RevReg have been applied, rejecting the transaction if the calculated accumulator does not match that from the issuer.

If an issuer's local revocation information gets out of sync with what is in the VDR, the issuer MUST rationalize the differences and produce a RevRegEntry transaction that accounts for both the last published RevRegEntry published in the VDR and the desired revocation status of all of the credentials in the RevReg.

The holder is not involved in the process of revoking a credential. There is no technical requirement for an issuer to notify the holder that a credential they were issued has been revoked. That said, it is a courtesy that may improve the user experience of the holder. Aries RFC 0183 Revocation Notification is an example of how that can be done. Even if not notified by the issuer of the revocation of a credential, the holder can detect their credential has been revoked when they retrieve the list of revoked credentials from the VDR and discover the index of their credential in the list.

§ AnonCreds Presentation Request with Revocation

Carrying out an AnonCreds presentation with revocation is a two-step process, beginning with a request from the verifier asking the holder to include a non-revocation proof (NRP) in the presentation, and then the holder creating the NRP and including it in the presentation sent to the verifier.

The verifier requesting a non-revocation proof, and the holder generating the non-revocation proof are covered in the sections of this specification about requesting and generating presentations, respectively.

§ AnonCreds Verification with Revocation

A verifier receives the presentation from the holder and processes the non-revocation-related parts of the presentation and the revocation-related parts of the presentation (if any) in the presentation. The resulting status of the presentation combines the verification outcomes from processing all proofs within the presentation. If verification of one or more of the embedded proofs is unsuccessful, the presentation is rejected as unverifiable.

§ AnonCreds Methods

In the AnonCreds data flows are specifications of data models that contain identifiers to public AnonCreds objects (Schemas, CredDefs, Rev_Reg_Defs and Rev_Reg_Entrys) that are published by issuers to locations (Verifiable Data Registries or VDRs) that must be accessible to holders and verifiers to enable presentation generation and verification. The format of the objects identifiers and the location of the objects are not defined in this specification. Rather, similar to the approach of DID Methods defined in the W3C DID Specification, AnonCreds methods allow for the registration and resolution mechanisms for AnonCreds objects across a range of VDRs. A registry of supported AnonCreds methods can be found in the AnonCreds Methods Registry.

Each AnonCreds method specifies the format of the object identifiers, to what Verifiable Data Registry the objects are published, how issuers register (publish) objects, and how issuers and verifiers can resolve the identifiers to retrieve the published objects. Implementations of agents (issuers, holders, verifiers) with AnonCreds support should be organized so as to allow issuers to use at least one AnonCreds method for registration, and to allow holders and verifiers to use one or more AnonCreds Methods for resolution. AnonCreds issuers will likely choose just a single AnonCreds registration method(s) they will use, and all AnonCreds participants will choose the set of AnonCreds resolvers they will require based on the issuers and types of credentials they want to support. As with DIDs, an external Universal AnonCreds Resolver is possible, as is a Universal AnonCreds Registrar.

§ AnonCreds Identifiers

AnonCreds identifiers SHOULD be a Uniform Resource Identifier (URI) conformant with RFC3986, although one notable exception is permitted. The exception is that for backwards compatibility, the AnonCreds identifiers used in the early (pre did:indy) open source Hyperledger Indy AnonCreds implementation are permitted. In the AnonCreds Method Registry, this is the Hyperledger Indy Legacy AnonCreds Method.

§ Revocation Support

Implementers only familiar with the “deltas”-style data format of Hyperledger Indy RevRegEntries may not be aware that other VDRs may store the contents of each RevRegEntry as “full state”, meaning the status of each credential in the registry (revoked or not) is stored, vs. only the differences from the previous RevRegEntry as in Hyperledger Indy. Either approach is fine as long as data is normalized by the AnonCreds method to the RevReg format expected for AnonCreds generate presentation processing. This allows a AnonCreds Methods to trade-off the size of the RevRegEntry in the VDR with the need for VDR-side processing to collect all of the deltas needed by the holder.

An AnonCreds Method may opt to not support revocation at all, and generate an error if the issuer attempts to create a CredDef that includes revocation support.

§ AnonCreds Method Registry

The AnonCreds Method Registry is published here. The registry contains a description, metadata and a link to a specification for each AnonCreds Method submitted by its implementers/maintainers. The registry is a web page generated from this repository.

The AnonCreds Methods registry repository and published registry is managed by the AnonCreds Specification Working Group based on this governance framework.

Each entry in the AnonCreds Method Registry links to a specification for the associated AnonCreds objects method. The method specifications must include information about the AnonCreds identifiers used by the method, along with the mechanisms for AnonCreds objects registration and resolution. In some cases, the AnonCreds method specification is defined within a DID Method specification, while in other cases, the AnonCreds method is a standalone specification.

§ Cryptographic protocols

§ Terminology

TODO: general terms here

§ Protocols

Most protocols described here are zero-knowledge proofs for different statements. They follow the patterns of three-move rounds protocols with a commit, a challenge and a response phase which we shall describe as three separate algorithms. The challenge phase is unique, whereas the commit and response phases have a specific instance for each of the statements being proved.

§ Issuer Setup

The issuer setup consists of a set of algorithms to generate the Issuer Public Key. The set of algorithms are:

The reference implementation of CredentialKeyGen is here.


( PK, SK ) = CredentialKeyGen( L )

Inputs:

 - L, the total number of attributes signed by this issuer

Parameters:

 -

Definitions:

 - l_n, the bitlength of the RSA modulus n of the issuer public key

Outputs:

 - PK, the credential public key of the issuer
 - SK, the credential secret key of the issuer

Procedure:

 1. p = SafePrime(l_n/2)                     # Generate a safe prime

 2. q = SafePrime(l_n/2)                     # Generate a safe prime

 3. n = p * q                                # The RSA modulus

 4. p' = (p-1)/2                             # The Sophie Germain prime of p

 5. q' = (q-1)/2                             # The Sophie Germain prime of q

 6. S = PRNG(l_n)^2 (mod n)                  # A quadratic residue mod n, it generates the subgroup mod n of size p' * q'

 7. Z = S^{PRNG(2, p'*q' - 1)} (mod n)       # The term Z of the CL public key

 8. R = ()                                   # Initialise the set of terms R_i of the CL public key

 9. foreach j in (1, ..., L):

10. R = R + {S^{PRNG(2, p'*q' - 1)} (mod n)} # Generate R_j

11. PK = ( n, S, Z, R )

12. SK = ( p, q )

13. return ( PK, SK )

§ Proving knowledge of a signature with selective disclosure of messages (ProveCL)

ProveCLCommit and ProveCLResponse are used by a Holder who possesses one or more signatures from one or more Issuers and uses them to derive a proof of knowledge for them. The algorithms are invoked once per signature.

TODO: considerations about the algorithm and its security

TODO: clarify exact format and encoding of inputs and outputs

The reference implementation of ProveCLCommit is here.


( A', v', e', Z~, e~, v~ ) = ProveCLCommit( PK, signature, (m_1,..., m_L), RevealedIndexes, R )

Inputs:

 - PK (REQUIRED), the public key of the issuer
 - signature (REQUIRED), the output of the issuance protocol
 - (m_1,..., m_L) (OPTIONAL), the set of signed messages
 - RevealedIndexes (OPTIONAL), indices of revealed messages
 - R (OPTIONAL), the set of random factors to blind unrevealed messages; each random has length l_m + l_0 + l_H

Parameters:

 - TBD

Definitions:

 - l_n, the bitlength of the RSA modulus n of the issuer public key
 - l_m, the bitlength of messages
 - l_e, the bitlength of the prime e
 - l_v, the bitlength of the randomization factor v
 - l'_e, the size of the interval in which e is chosen
 - l_0, security parameter that governs the statistical zero-knowledge property
 - l_H, the bitsize of values hashed by the hash function H used for the Fiat-Shamir heuristic
 - L, the number of messages signed by the issuer

Outputs:

 - A', term of the rerandomised signature
 - v', term of the rerandomised signature
 - e', term of the rerandomised signature
 - Z~. t-value for the signature
 - e~, randomness used to generate t-value
 - v~, randomness used to generate t-value

Procedure:

 1. (i1, i2,..., iR) = RevealedIndexes       # the indices of messages that are revealed in this proof

 2. (j1, j2,..., jU) = [L] \ RevealedIndexes # the indices of messages that are kept hidden

 3. (m~_1, m~_2,..., m~_U) = R               # the random factors blinding each hidden message

 4. (n, S, Z, R_1, ..., R_L) = PK            # unpack the issuer public key

 5. (A, e, v) = signature                    # unpack the signature

 6. r = PRNG(l_n + l_0)                      # choose random to blind the signature

 7. A' = A * S^r mod n                       # compute the randomised signature

 8. v' = v - e * r                           # recompute v given the randomisation

 9. e' = e - 2^{l_e - 1}                     # prepare value to prove that e is positive

10. e~ = PRNG(l'_e + l_0 + l_H)              # random for t-value of the signature

11. v~ = PRNG(l_v + l_0 + l_H)               # random for t-value of the signature

12. Z~ = A'^{e~} * S^{v~} mod n              # compute t-value for the signature

13. foreach j in (j1, j2,..., jU):

14.     Z~ = Z~ * R_j^{m~_j} mod n           # add component for each undisclosed message

15. return ( A', v', e', Z~, e~, v~ )

The reference implementation of ProveCLResponse is here.


pi = ProveCLResponse( (m_1,..., m_L), RevealedIndexes, R, c, ( A', v', e', Z~, e~, v~ ) )

Inputs:

 - (m_1,..., m_L) (OPTIONAL), the set of signed messages
 - R (OPTIONAL), the set of random factors to blind unrevealed messages; each random has length l_m + l_0 + l_H
 - c, an octet string representing the Fiat-Shamir challenge value
 - A', term of the rerandomised signature
 - v', term of the rerandomised signature
 - e', term of the rerandomised signature
 - Z~. t-value for the signature
 - e~, randomness used to generate t-value
 - v~, randomness used to generate t-value

Parameters:

 - TBD

Definitions:

 - TBD

Outputs:

 - A', term of the rerandomised signature
 - Z~. t-value for the signature
 - e^, s-value for the signature
 - v^, s-value for the signature
 - (m^_1,..., m^_L), s-value for the signature

Procedure:

 1. (j1, j2,..., jU) = [L] \ RevealedIndexes # the indices of messages that are kept hidden

 2. (m~_1, m~_2,..., m~_U) = R               # the random factors blinding each hidden message

 3. v^ = v~ + c * v'                         # the response for the term v'

 4. e^ = e~ + c * e'                         # the response for the term e'

 5. foreach j in (j1, j2,..., jU):

 6.     m^_j = m~_j + c * m_j                # the response for the undisclosed messages

 7. pi = (A', Z~, e^, v^, (m^_1,..., m^_L) ) # the terms that constitute the proof that will be verified

 8. return pi

§ AnonCreds Conventions

TODO

Cover conventions like encoding claims, date handling for predicates and revocation status requests

§ IANA Considerations

This document has no IANA actions.

§ Security Considerations

TODO

Add security considerations.

§ Privacy Considerations

TODO

Add privacy considerations.

§ References

§ Normative References

TODO

Add normative references

§ Informative References

TODO

Add informative references

§ Resources on cryptography

§ Sigma protocols

§ Acknowledgements

AnonCreds was initially created as part of the Open Source Hyperledger Indy project.

This specification is the work of the AnonCreds Working Group, which includes dozens of active and dedicated participants. In particular, the following individuals contributed ideas, feedback, and wording that influenced this specification:

§ Authors’ Addresses

TODO

Add authors’ addresses.

Table of Contents