Skip to main content

With Identity Rust 0.6.3 and Wasm 0.6.1 a breaking change was introduced that requires a migration of existing Stronghold Snapshot files. You can find migration instructions here.

Version: 0.6

Issuance

info

The IOTA DIDComm Specification is in the RFC phase and may undergo changes. Suggestions are welcome at GitHub #464.

  • Version: 0.1
  • Status: IN-PROGRESS
  • Last Updated: 2021-10-29

Overview

Allows a holder to request a verifiable credential from an issuer. The issuer may alternatively initiate the issuance without a request from the holder. This protocol also allows the issuer to request additional information and to offload the actual signing to a different party.

Relationships

Example Use-Cases

  • A university issues a degree to a graduate that can be verified by potential employers.
  • A resident requests proof of address from their city council.
  • An insurer issues proof that a company has liability insurance.

Roles

  • Holder: stores one or more verifiable credentials. A holder is usually but not always the subject of those credentials.
  • Issuer: creates verifiable credentials asserting claims about one or more subjects, transmitted to a holder.

Interaction

IssuanceDiagram

For guidance on diagrams see the corresponding section in the overview.

Messages

1. issuance-request

  • Type: iota/issuance/0.1/issuance-request
  • Role: holder

The holder requests a single verifiable credential from the issuer of a particular kind.

Structure

{
"subject": DID, // REQUIRED
"credentialInfo": CredentialInfo, // REQUIRED
}
FieldDescriptionRequired
subjectDID of the credential subject1.
credentialInfoA CredentialInfo object, specifying a credential kind requested by the holder.2 3 4

1 The holder is usually but not always the subject of the requested credential. There may be custodial, legal guardianship, or delegation situations where a third-party requests, or is issued a credential on behalf of a subject. It is the responsibility of the issuer to ensure authorization in such cases.

2 The credentialInfo could be hard-coded, communicated in-band, discovered out-of-band, or be pre-sent by an issuer. The issuer SHOULD reject the request with a problem-report if it does not support the requested credentialInfo.

3 With CredentialType2021, the type MAY be under-specified if the exact type is unknown or if the resulting type depends on the identity or information of the subject or holder. E.g. the type could be as general as ["VerifiableCredential"] if the issuer issues only a singular type of credential or decides the credential based on other information related to the subject.

4 With CredentialType2021, the holder MAY specify one or more trusted issuers they would like to sign the resulting credential. The issuer SHOULD reject the request with a problem-report if it supports none of the requested issuer entries. However, there are circumstances where an issuer is no longer supported or was compromised, so this behavior should be decided based on the application.

An issuer wanting to preserve privacy regarding which exact credential kinds, types, or issuers they support should be careful with the information they disclose in problem-reports when rejecting requests. E.g. a problem-report with only a reject-request descriptor discloses less information than the reject-request.invalid-type or reject-request.invalid-trusted-issuer descriptors, as the latter two could be used to determine supported types or signing issuers by process of elimination.

Examples

  1. Request any drivers licence credential using CredentialType2021:
{
"subject": "did:example:c6ef1fe11eb22cb711e6e227fbc",
"credentialInfo": {
"credentialInfoType": "CredentialType2021",
"type": ["VerifiableCredential", "DriversLicence"]
}
}
  1. Request a university degree credential from either supported trusted issuer using CredentialType2021:
{
"subject": "did:example:c6ef1fe11eb22cb711e6e227fbc",
"credentialInfo": {
"credentialInfoType": "CredentialType2021",
"type": [
"VerifiableCredential",
"UniversityDegreeCredential",
"BachelorOfArtsDegreeCredential"
],
"issuer": [
"did:example:76e12ec712ebc6f1c221ebfeb1f",
"did:example:f1befbe122c1f6cbe217ce21e67"
]
}
}

2. issuance-offer

  • Type: iota/issuance/0.1/issuance-offer
  • Role: issuer

The issuer offers a single, unsigned credential to the holder, matching the preceding issuance-request if present. The issuer may set an expiry date for the offer and require non-repudiable proof by the holder that the offer was received.

Structure

{
"unsignedCredential": Credential, // REQUIRED
"offerChallenge": {
"challenge": string, // REQUIRED
"credentialHash": string, // REQUIRED
}, // OPTIONAL
"offerExpiry": DateTime // OPTIONAL
}
FieldDescriptionRequired
unsignedCredentialUnsigned credential being offered to the holder. This MUST NOT include a proof section.
offerChallengeIf present, indicates the issuer requires the acceptance of the credential to be signed by the holder in the following issuance-response for non-repudiation.1
challengeA random string that should be unique per issuance-offer.
credentialHashThe Base58-encoded SHA-256 digest of the unsignedCredential formatted according to the JSON Canonicalization Scheme.
offerExpiryA string formatted as an XML DateTime normalized to UTC 00:00:00 and without sub-second decimal precision. E.g: "2021-12-30T19:17:47Z".2

1 Issuing challenges should be done with due consideration to security and privacy concerns: not all applications require non-repudiation to third-parties and a holder may wish to deny that they ever requested or accepted a particular credential. The challenge SHOULD NOT be used for authentication of the holder; see the authentication protocol and sender authenticated encryption.

2 If present, an offerExpiry indicates that the issuer MAY rescind the offer and abandon the protocol if an affirmative issuance-response is not received before the specified datetime. Note that the offerExpiry should override any default message timeouts.

Examples

  1. Offer a degree credential:
{
"unsignedCredential": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "6c1a1477-e452-4da7-b2db-65ad0b369d1a",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
"issuanceDate": "2021-05-03T19:73:24Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science and Arts"
}
}
}
}
  1. A time-limited offer for a degree credential with a signature requested:
{
"unsignedCredential": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "6c1a1477-e452-4da7-b2db-65ad0b369d1a",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
"issuanceDate": "2021-01-05T19:37:24Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science and Arts"
}
}
},
"offerChallenge": {
"challenge": "d7b7869e-fec3-4de9-84bb-c3a43bacff33",
"credentialHash": "28Ae7AdqzyMyF9pmnwUNK1Q7VT3EzDDGEj1Huk7uYQT94KYAhQzEPyhoF5Ugs3totUugLPpghGmE9HaG8usJZcZv"
},
"offerExpiry": "2021-01-05T20:07:24Z"
}

3. issuance-response

  • Type: iota/issuance/0.1/issuance-response
  • Role: holder

The holder responds to a issuance-offer by accepting or disputing the offer and optionally signing the response for non-repudiation.

Structure

{
"accepted": bool, // REQUIRED
"disputes": [Dispute], // OPTIONAL
"signature": {
"offerChallenge": {
"challenge": string, // REQUIRED
"credentialHash": string, // REQUIRED
}, // REQUIRED
"proof": Proof, // REQUIRED
} // OPTIONAL
}
FieldDescriptionRequired
acceptedIndicates if the holder accepts the offered credential from issuance-offer. MUST be false if any disputes are present.
disputesAllows the holder to dispute one or more claims in the credential.
signatureThis SHOULD be present if a offerChallenge was included in the preceding issuance-offer.1
offerChallengeThis MUST match the offerChallenge in the preceding issuance-offer.
proofSignature of the holder on the offerChallenge.

1 A valid signature allows the issuer to prove that the credential was accepted by the holder. If present, the issuer MUST validate the proof is correct and signed with an unrevoked verification method, and issue a problem-report if not. The issuer SHOULD terminate the protocol if no signature is present and a offerChallenge was included in the preceding issuance-offer message. An explicit signature is used instead of a signed DIDComm message to avoid the need to store the entire credential for auditing purposes; the hash is sufficient to prove a particular credential was accepted.

Examples

  1. Accept a credential offer:
{
"accepted": true,
"disputes": []
}
  1. Accept a credential offer including a signature:
{
"accepted": true,
"disputes": [],
"signature": {
"offerChallenge": {
"challenge": "d7b7869e-fec3-4de9-84bb-c3a43bacff33",
"credentialHash": "28Ae7AdqzyMyF9pmnwUNK1Q7VT3EzDDGEj1Huk7uYQT94KYAhQzEPyhoF5Ugs3totUugLPpghGmE9HaG8usJZcZv",
},
"proof": {...}
}
}
  1. Reject a credential offer with disputes:
{
"accepted": false,
"disputes": [{
"@context": [
"https://www.w3.org/2018/credentials/v1",
],
"id": "6e8e989e-749e-4ed8-885b-b2a2bb64835f",
"type": ["VerifiableCredential", "DisputeCredential"],
"credentialSubject": {
"id": "6c1a1477-e452-4da7-b2db-65ad0b369d1a",
"currentStatus": "Disputed",
"statusReason": {
"value": "Incorrect name.",
"lang": "en"
},
},
"issuer": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"issuanceDate": "2021-01-05T19:46:24Z",
"proof": {...}
}],
}

4. issuance

  • Type: iota/issuance/0.1/issuance
  • Role: issuer

The issuer transmits the signed credential following a issuance-response by the holder. The issuer may set an expiry until when they expect an acknowledgment and request a cryptographic signature in the acknowledgment for non-repudiation.

Structure

{
"signedCredential": Credential, // REQUIRED
"issuanceChallenge": {
"challenge": string, // REQUIRED
"credentialHash": string, // REQUIRED
}, // OPTIONAL
"issuanceExpiry": DateTime, // OPTIONAL
}
FieldDescriptionRequired
signedCredentialVerifiable credential signed by the issuer.1
issuanceChallengeIf present, indicates the issuer requires the issuance-acknowledgement of the credential to be signed for non-repudiation.
challengeA random string that should be unique per issuance.
credentialHashThe Base58-encoded SHA-256 digest of the signedCredential, including the proof, formatted according to the JSON Canonicalization Scheme.
issuanceExpiryA string formatted as an XML Datetime normalized to UTC 00:00:00 and without sub-second decimal precision indicating when the offer expires. E.g: "2021-12-30T19:17:47Z".2

1 The holder SHOULD validate both that the proof on the signedCredential is correctly signed by a trusted issuer and that the contents match those of the unsignedCredential from the issuance-offer they accepted. If not, a relevant problem-report should be sent.

2 The issuer SHOULD send a problem-report if the issuanceExpiry datetime passes without receiving an issuance-acknowledgement message from the holder. The issuer MAY revoke the credential in this case.

Examples

  1. Issuing a credential including expiry and requesting proof:
{
"unsignedCredential": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "6c1a1477-e452-4da7-b2db-65ad0b369d1a",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
"issuanceDate": "2021-01-05T19:37:24Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science and Arts"
}
},
"proof": {
"type": "JcsEd25519Signature2020",
"verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#key",
"signatureValue": "3KpeHSW4LybMy1smFEYriRmj5FsFfnxQiEsBnQdYzwkXMnjF3Jjn5RS1KGzheNpUgHW5yua8DoLbfYmZFAvaUVwv"
}
},
"issuanceChallenge": {
"challenge": "6ff5f616-2f9c-4e47-b9d2-5553deeac01d",
"credentialHash": "21DtABsnYNb7oGEY8aybb9Bghq6NJJWvrQgtC2SBdhgQ8v6cZGjnT8RmEmBLZfHyfEYMAik3D1EoNQZCaT4RUKEX"
},
"issuanceExpiry": "2021-01-05T20:07:24Z"
}

5. issuance-acknowledgment

  • Type: iota/issuance/0.1/issuance-acknowledgment
  • Role: holder

The holder confirms receipt of a successful credential issuance, optionally including non-repudiable proof.

Structure

{
"signature": {
"issuanceChallenge": {
"challenge": string, // REQUIRED
"credentialHash": string, // REQUIRED
}, // REQUIRED
"proof": Proof, // REQUIRED
} // OPTIONAL
}
FieldDescriptionRequired
signatureThis SHOULD be present if a issuanceChallenge was included in the preceding issuance message.1
issuanceChallengeThis MUST match the issuanceChallenge in the preceding issuance message.
proofSignature of the holder on the issuanceChallenge.

1 The issuer MUST validate the signature and MAY revoke the issued credential if a signature was requested, e.g. for non-repudiation or auditing, and not received or an invalid signature is received. An explicit signature is used instead of a signed DIDComm message to avoid the need to store the entire credential for auditing purposes as the hash is sufficient to prove a particular credential was accepted.

Examples

  1. Acknowledge receipt of the credential:
{}
  1. Acknowledge receipt of the credential including a signature:
{
"signature": {
"issuanceChallenge": {
"challenge": "6ff5f616-2f9c-4e47-b9d2-5553deeac01d",
"credentialHash": "21DtABsnYNb7oGEY8aybb9Bghq6NJJWvrQgtC2SBdhgQ8v6cZGjnT8RmEmBLZfHyfEYMAik3D1EoNQZCaT4RUKEX",
},
"proof": {...}
}
}

Problem Reports

The following problem-report codes may be raised in the course of this protocol and are expected to be recognised and handled in addition to any general problem-reports. Implementers may also introduce their own application-specific problem-reports.

For guidance on problem-reports and a list of general codes see problem reports.

CodeMessageDescription
e.p.msg.iota.issuance.reject-requestissuance-requestIssuer rejects a credential request for any reason, e.g. unrecognised or invalid type, trusted issuer, or subject.
e.p.msg.iota.issuance.reject-request.invalid-subjectissuance-requestIssuer rejects a credential request due to the subject being unrecognised, missing, or otherwise invalid.
e.p.msg.iota.issuance.reject-request.invalid-typeissuance-requestIssuer rejects a credential request due to the type or @context being unsupported or otherwise invalid.
e.p.msg.iota.issuance.reject-request.invalid-issuerissuance-requestIssuer rejects a credential request due to trustedIssuers being unrecognised, unsupported or otherwise invalid.
e.p.msg.iota.issuance.presentation-failedissuance-offerIssuer terminates the protocol due to a failed presentation request for more information prior to a issuance-offer.
e.p.msg.iota.issuance.reject-response.missing-signatureissuance-responseIssuer rejects an issuance-response missing a signature when offerChallenge was included in the preceding issuance-offer message.
e.p.msg.iota.issuance.reject-issuanceissuanceHolder rejects a credential issuance for any reason, e.g. mismatch with the credential in the issuance-offer. Note that disputes are handled in issuance-response prior to issuance.
e.p.msg.iota.issuance.expiredissuanceIssuer notifies the holder that an issuance message has expired without a valid issuance-acknowledgement.
e.p.msg.iota.issuance.reject-acknowledgement.missing-signatureissuance-acknowledgementIssuer rejects an issuance-acknowledgement missing a signature when issuanceChallenge was included in the preceding issuance message.

Unresolved Questions

  • The credentialSubject::id field of a verifiable credential is optional and not always a DID according to the verifiable credential specification. Should we enforce that it is always a DID? This affects presentations are noted in the subject-holder relationships section of the specification. We essentially enforce the nonTransferable property for all credentials in our presentations currently to prevent verifiers storing and re-presenting credentials as their own.

  • e.p.msg.iota.issuance.reject-request.invalid-type and e.p.msg.iota.issuance.reject-request.invalid-issuer are specific to CredentialType2021. Should they be listed here? If yes, should they be marked accordingly?

Further Reading