- Authors: Nikita Khateev
- Status: ACCEPTED
- Since: 2019-05-28
- Status Note: See RFC 0037 for the presentation part of using credentials.
- Supersedes: Indy HIPE PR #89; also Credential Exchange 0.1 -- IIW 2019
- Start Date: 2019-01-30
- Tags: feature, decorator, protocol, credentials, test-anomaly
In version 1.1 of the propose-credential message, the following optional fields were added: schema_name, schema_version, and issuer_did.
The previous version is 1.0/propose-credential.
Formalizes messages used to issue a credential--whether the credential is JWT-oriented, JSON-LD-oriented, or ZKP-oriented. The general flow is similar, and this protocol intends to handle all of them. If you are using a credential type that doesn't fit this protocol, please raise a Github issue.
We need a standard protocol for issuing credentials. This is the basis of interoperability between Issuers and Holders.
There are two roles in this protocol: Issuer and Holder. Technically, the latter role is only potential until the protocol completes; that is, the second party becomes a Holder of a credential by completing the protocol. However, we will use the term Holder throughout, to keep things simple.
Note: When a holder of credentials turns around and uses those credentials to prove something, they become a Prover. In the sister RFC to this one, 0037: Present Proof, the Holder is therefore renamed to Prover. Sometimes in casual conversation, the Holder role here might be called "Prover" as well, but more formally, "Holder" is the right term at this phase of the credential lifecycle.
The choreography diagrams shown below detail how state evolves in this protocol, in a "happy path." The states include:
- proposal-received
- offer-sent
- request-received
- credential-issued
- done
- proposal-sent
- offer-received
- request-sent
- credential-received
- done
Errors might occur in various places. For example, an Issuer might offer a credential for a price that the Holder is unwilling to pay. All errors are modeled with a problem-report
message. Easy-to-anticipate errors reset the flow as shown in the diagrams, and use the code issuance-abandoned
; more exotic errors (e.g., server crashed at Issuer headquarters in the middle of a workflow) may have different codes but still cause the flow to be abandoned in the same way. That is, in this version of the protocol, all errors cause the state of both parties (the sender and the receiver of the problem-report
) to revert to null
(meaning it is no longer engaged in the protocol at all). Future versions of the protocol may allow more granular choices (e.g., requesting and receiving a (re-)send of the issue-credential
message if the Holder times out while waiting in the request-sent
state).
The Issue Credential protocol consists of these messages:
propose-credential
- potential Holder to Issuer (optional). Tells what the Holder hopes to receive.offer-credential
- Issuer to potential Holder (optional for some credential implementations; required for Hyperledger Indy). Tells what the Issuer intends to issue, and possibly, the price the Issuer expects to be paid.request-credential
- potential Holder to Issuer. If neither of the previous message types is used, this is the message that begins the protocol.issue-credential
- Issuer to new Holder. Attachment payload contains the actual credential.
In addition, the ack
and problem-report
messages are adopted into the protocol for confirmation and error handling.
Note: This protocol is about the messages that must be exchanged to issue verifiable credentials, NOT about the specifics of particular verifiable credential schemes. DIDComm attachments are deliberately used in messages to isolate the protocol flow/semantics from the credential artifacts themselves as separate constructs. Attachments allow credential formats and this protocol to evolve through versioning milestones independently instead of in lockstep. Links are provided in the message descriptions below, to describe how the protocol adapts to specific verifiable credential implementations.
Note: This diagram was made in draw.io. To make changes:
- upload the drawing HTML from this folder to the draw.io site (Import From...GitHub),
- make changes,
- export the picture and HTML to your local copy of this repo, and
- submit a pull request.
The protocol has 3 alternative beginnings:
- The Issuer can begin with an offer.
- The Holder can begin with a proposal.
- the Holder can begin with a request.
The offer and proposal messages are part of an optional negotiation phase and may trigger back-and-forth counters. A request is not subject to negotiation; it can only be accepted or rejected.
An optional message sent by the potential Holder to the Issuer to initiate the protocol or in response to a offer-credential
message when the Holder wants some adjustments made to the credential data offered by Issuer.
Note: In Hyperledger Indy, where the
request-credential
message can only be sent in response to anoffer-credential
message, thepropose-credential
message is the only way for a potential Holder to initiate the workflow.
Schema:
{
"@type": "https://didcomm.org/issue-credential/1.1/propose-credential",
"@id": "<uuid-of-propose-message>",
"comment": "some comment",
"credential_proposal": <json-ld object>,
"schema_issuer_did": "DID of the proposed schema issuer",
"schema_id": "Schema ID string",
"schema_name": "Schema name string",
"schema_version": "Schema version string",
"cred_def_id": "Credential Definition ID string"
"issuer_did": "DID of the proposed issuer"
}
Description of attributes:
comment
-- an optional field that provides human readable information about this Credential Proposal, so the proposal can be evaluated by human judgment. Follows DIDComm conventions for l10n.credential_proposal
-- an optional JSON-LD object that represents the credential data that Prover wants to receive. It matches the schema of Credential Preview.schema_issuer_did
-- optional filter to request credential based on a particular Schema issuer DID.schema_id
-- optional filter to request credential based on a particular Schema. This might be helpful when requesting a version 1 passport instead of a version 2 passport, for example.schema_name
-- optional filter to request credential based on a schema name. This is useful to allow a more user-friendly experience of requesting a credential by schema name.schema_version
-- optional filter to request credential based on a schema version. This is useful to allow a more user-friendly experience of requesting a credential by schema name and version.cred_def_id
-- optional filter to request credential based on a particular Credential Definition. This might be helpful when requesting a commercial driver's license instead of an ordinary driver's license, for example.issuer_did
-- optional filter to request a credential issued by the owner of a particular DID.
A message sent by the Issuer to the potential Holder, describing the credential they intend to offer and possibly the price they expect to be paid. In Hyperledger Indy, this message is required, because it forces the Issuer to make a cryptographic commitment to the set of fields in the final credential and thus prevents Issuers from inserting spurious data. In credential implementations where this message is optional, an Issuer can use the message to negotiate the issuing following receipt of a request-credential
message.
Schema:
{
"@type": "https://didcomm.org/issue-credential/1.0/offer-credential",
"@id": "<uuid-of-offer-message>",
"comment": "some comment",
"credential_preview": <json-ld object>,
"offers~attach": [
{
"@id": "libindy-cred-offer-0",
"mime-type": "application/json",
"data": {
"base64": "<bytes for base64>"
}
}
]
}
Description of fields:
comment
-- an optional field that provides human readable information about this Credential Offer, so the offer can be evaluated by human judgment. Follows DIDComm conventions for l10n.credential_preview
-- a JSON-LD object that represents the credential data that Issuer is willing to issue. It matches the schema of Credential Preview;offers~attach
-- an array of attachments that further define the credential being offered. This might be used to clarify which formats or format versions will be issued.- For Indy, the attachment includes a nonce and key correctness proof to facilitate integrity checks. It is a base64url-encoded version of the data returned from
indy_issuer_create_credential_offer()
.
- For Indy, the attachment includes a nonce and key correctness proof to facilitate integrity checks. It is a base64url-encoded version of the data returned from
The Issuer may add a ~payment-request
decorator to this message to convey the need for payment before issuance. See the payment section below for more details.
It is possible for an Issuer to add a ~timing.expires_time
decorator to this message to convey the idea that the offer will expire at a particular point in the future. Such behavior is not a special part of this protocol, and support for it is not a requirement of conforming implementations; the ~timing
decorator is simply a general possibility for any DIDComm message. We mention it here just to note that the protocol can be enriched in composable ways.
This is a message sent by the potential Holder to the Issuer, to request the issuance of a credential. Where circumstances do not require a preceding Offer Credential message (e.g., there is no cost to issuance that the Issuer needs to explain in advance, and there is no need for cryptographic negotiation), this message initiates the protocol. In Hyperledger Indy, this message can only be sent in response to an Offer Credential message.
Schema:
{
"@type": "https://didcomm.org/issue-credential/1.0/request-credential",
"@id": "<uuid-of-request-message>",
"comment": "some comment",
"requests~attach": [
{
"@id": "attachment id",
"mime-type": "application/json",
"data": {
"base64": "<bytes for base64>"
}
},
]
}
Description of Fields:
comment
-- an optional field that provides human readable information about this Credential Request, so it can be evaluated by human judgment. Follows DIDComm conventions for l10n.requests~attach
-- an array of attachments defining the requested formats for the credential.- For Indy, the attachment is a base64url-encoded version of the data returned from
indy_prover_create_credential_req()
.
- For Indy, the attachment is a base64url-encoded version of the data returned from
This message may have a ~payment-receipt
decorator to prove to the Issuer that the potential Holder has satisfied a payment requirement. See the payment section below.
This message contains as attached payload the credentials being issued and is sent in response to a valid Request Credential message.
Schema:
{
"@type": "https://didcomm.org/issue-credential/1.0/issue-credential",
"@id": "<uuid-of-issue-message>",
"comment": "some comment",
"credentials~attach": [
{
"@id": "libindy-cred-0",
"mime-type": "application/json",
"data": {
"base64": "<bytes for base64>"
}
}
]
}
Description of fields:
comment
-- an optional field that provides human readable information about the issued credential, so it can be evaluated by human judgment. Follows DIDComm conventions for l10n.credentials~attach
-- an array of attachments containing the issued credentials.- For Indy, the attachment contains data from libindy about credential to be issued, base64url-encoded, as returned from
libindy
. For more information see the Libindy API.
- For Indy, the attachment contains data from libindy about credential to be issued, base64url-encoded, as returned from
If the issuer wants an acknowledgement that the issued credential was accepted, this message must be decorated with ~please-ack
, and it is then best practice for the new Holder to respond with an explicit ack
message as described in 0317: Please ACK Decorator.
Claims in Hyperledger Indy-based verifiable credentials are put into the credential in two forms, raw
and encoded
. raw
is the actual data value, and encoded
is the (possibly derived) integer value that is used in presentations. At this time, Indy does not take an opinion on the method used for encoding the raw value. This will change with the Rich Schema work that is underway in the Indy/Aries community, where the encoding method will be part of the credential metadata available from the public ledger.
Until the Rich Schema mechanism is deployed, Aries issuers and verifiers must agree on the encoding method so that the verifier can check that the raw
value returned in a presentation corresponds to the proven encoded
value. The following is the encoding algorithm that MUST be used by Issuers when creating credentials and SHOULD be verified by Verifiers receiving presentations:
- keep any 32-bit integer as is
- for data of any other type:
- convert to string (use string "None" for null)
- encode via utf-8 to bytes
- apply SHA-256 to digest the bytes
- convert the resulting digest bytes, big-endian, to integer
- stringify the integer as a decimal.
An example implementation in Python can be found here.
A gist of test value pairs can be found here.
This is not a message but an inner object for other messages in this protocol. It is used construct a preview of the data for the credential that is to be issued. Its schema follows:
{
"@type": "https://didcomm.org/issue-credential/1.0/credential-preview",
"attributes": [
{
"name": "<attribute name>",
"mime-type": "<type>",
"value": "<value>"
},
// more attributes
]
}
The main element is attributes
. It is an array of (object) attribute specifications; the subsections below outline their semantics.
The mandatory "name"
key maps to the attribute name as a string.
The optional mime-type
advises the issuer how to render a binary attribute, to judge its content for applicability before issuing a credential containing it. Its value parses case-insensitively in keeping with MIME type semantics of RFC 2045. If mime-type
is missing, its value is null.
The mandatory value
holds the attribute value:
- if
mime-type
is missing (null), thenvalue
is a string. In other words, implementations interpret it the same as any other key+value pair in JSON - if
mime-type
is not null, thenvalue
is always a base64url-encoded string that represents a binary BLOB, andmime-type
tells how to interpret the BLOB after base64url-decoding.
Threading can be used to initiate a sub-protocol during an issue credential protocol instance. For example, during credential issuance, the Issuer may initiate a child message thread to execute the Present Proof
sub-protocol to have the potential Holder (now acting as a Prover) prove attributes about themselves before issuing the credential. Depending on circumstances, this might be a best practice for preventing credential fraud at issuance time.
If threading were added to all of the above messages, a ~thread
decorator would be present, and later messages in the flow would reference the @id
of earlier messages to stitch the flow into a single coherent sequence. Details about threading can be found in the 0008: Message ID and Threading RFC.
Credentialing ecosystems may wish to associate credential issuance with payments by fiat currency or tokens. This is common with non-digital credentials today; we pay a fee when we apply for a passport or purchase a plane ticket. Instead or in addition, some circumstances may fit a mode where payment is made each time a credential is used, as when a Verifier pays a Prover for verifiable medical data to be used in research, or when a Prover pays a Verifier as part of a workflow that applies for admittance to a university. For maximum flexibility, we mention payment possibilities here as well as in the sister 0037: Present Proof RFC.
Wherever they happen and whoever they involve, payments are accomplished with optional payment decorators. See 0075: Payment Decorators.
A ~payment-request
may decorate a Credential Offer from Issuer to Holder. When they do, a corresponding ~payment-receipt
should be provided on the Credential Request returned to the Issuer.
During credential presentation, the Verifier may pay the Holder as compensation for Holder for disclosing data. This would require a ~payment-request
in a Presentation Proposal message, and a corresponding ~payment-receipt
in the subsequent Presentation Request. If such a workflow begins with the Presentation Request, the Prover may sending back a Presentation (counter-)Proposal with appropriate decorator inside it.
Smart contracts may be missed in ecosystem, so operation "issue credential after payment received" is not atomic. It’s possible case that malicious issuer will charge first and then will not issue credential in fact. But this situation should be easily detected and appropriate penalty should be applied in such type of networks.
Negotiation prior to issuing the credential can be done using the offer-credential
and propose-credential
messages. A common negotiation use case would be about the data to go into the credential. For that, the credential_preview
element is used.
- VCX -- this implementation might not be perfect and needs to be improved, you can gather some info on parameters' purpose from it
- A pre-RFC (labelled version 0.1) implementation of the protocol was implemented by a number of groups in the Hyperledger Indy community leading up to IIW28 in April 2019. The protocol defined and implemented can be reviewed here. It was the basis of the IIWBook demo from BC Gov and collaborators.
Why should we not do this?
- Why is this design the best in the space of possible designs?
- What other designs have been considered and what is the rationale for not choosing them?
- What is the impact of not doing this?
Similar (but simplified) credential exchanged was already implemented in von-anchor.
- We might need to propose a new MIME type for credential (the same way as .docx is not processed as generic xml). The issue in W3C/vc-data-model: w3c/vc-data-model#421
- It is a common practice when changing some attributes in credential to revoke the old credential and issue a new one. It might be useful to have an element in the
offer-credential
message to indicate a connection between a now revoked credential and the new credential being offered. - We might need some explicit documentation for nested
@type
fields. - There should be a way to ask for some payment with
offer-credential
and to send a payment (or payment receipt) in the request-credential.
The following lists the implementations (if any) of this RFC. Please do a pull request to add your implementation. If the implementation is open source, include a link to the repo or to the implementation within the repo. Please be consistent in the "Name" field so that a mechanical processing of the RFCs can generate a list of all RFCs supported by an Aries implementation.
Name / Link | Implementation Notes |
---|---|
Streetcred.id | Commercial mobile and web app built using Aries Framework - .NET MISSING test results |