Aries RFC 0023: DID Exchange v1¶
- Authors: Ryan West, Daniel Bluhm, Matthew Hailstone, Sam Curren, Stephen Curran, George Aristy
- Status: ADOPTED
- Since: 2021-04-15
- Status Note: Replaces RFC 0160 - Connection Protocol and is a part of AIP 2.0.
- Supersedes: RFC 0160 - Connection Protocol
- Start Date: 2018-06-29
- Tags: feature, protocol, test-anomaly
Summary¶
This RFC describes the protocol to exchange DIDs between agents when establishing a DID based relationship.
Motivation¶
Aries agent developers want to create agents that are able to establish relationships with each other and exchange secure information using keys and endpoints in DID Documents. For this to happen there must be a clear protocol to exchange DIDs.
Version Change Log¶
Version 1.1 - Signed Rotations without DID Documents¶
Added the optional did_rotate~attach
attachment for provenance of rotation without an attached DID Document.
Tutorial¶
We will explain how DIDs are exchanged, with the roles, states, and messages required.
Roles¶
The DID Exchange Protocol uses two roles: requester and responder.
The requester is the party that initiates this protocol after receiving an invitation
message
(using RFC 0434 Out of Band) or by using an implied invitation from a public DID. For
example, a verifier might get the DID of the issuer of a credential they are verifying, and use information in the
DIDDoc for that DID as the basis for initiating an instance of this protocol.
Since the requester receiving an explicit invitation may not have an Aries agent, it is desirable, but not strictly, required that sender of the invitation (who has the responder role in this protocol) have the ability to help the requester with the process and/or costs associated with acquiring an agent capable of participating in the ecosystem. For example, the sender of an invitation may often be sponsoring institutions.
The responder, who is the sender of an explicit invitation or the publisher of a DID with an implicit invitation, must have an agent capable of interacting with other agents via DIDComm.
In cases where both parties already possess SSI capabilities, deciding who plays the role of requester and responder might be a casual matter of whose phone is handier.
States¶
Requester¶
The requester goes through the following states per the State Machine Tables below
- start
- invitation-received
- request-sent
- response-received
- abandoned
- completed
Responder¶
The responder goes through the following states per the State Machine Tables below
- start
- invitation-sent
- request-received
- response-sent
- abandoned
- completed
State Machine Tables¶
The following are the requester and responder state machines.
The invitation-sent
and invitation-received
are technically outside this protocol, but are useful to show in the state machine as the invitation is the trigger to start the protocol and is referenced from the protocol as the parent thread (pthid
). This is discussed in more detail below.
The abandoned
and completed
states are terminal states and there is no expectation that the protocol can be continued (or even referenced) after reaching those states.
Errors¶
After receiving an explicit invitation, the requester may send a problem-report
to the responder using the information in the invitation to either restart the invitation process (returning to the start
state) or to abandon the protocol. The problem-report
may be an adopted Out of Band
protocol message or an adopted DID Exchange
protocol message, depending on where in the processing of the invitation the error was detected.
During the request
/ response
part of the protocol, there are two protocol-specific error messages possible: one for an active rejection and one for an unknown error. These errors are sent using a problem_report message type specific to the DID Exchange Protocol. These errors do not transition the protocol to the abandoned
state. The following list details problem-code
s that may be sent in these cases:
request_not_accepted - The error indicates that the request
message has been rejected for a reason listed in the error_report
. Typical reasons include not accepting the method of the provided DID, unknown endpoint protocols, etc. The request
can be resent after the appropriate corrections have been made.
request_processing_error - This error is sent when the responder was processing the request with the intent to accept the request, but some processing error occurred. This error indicates that the request
should be resent as-is.
response_not_accepted - The error indicates that the response
has been rejected for a reason listed in the error_report
. Typical reasons include not accepting the method of the provided DID, unknown endpoint protocols, invalid signature, etc. The response
can be resent after the appropriate corrections have been made.
response_processing_error - This error is sent when the requester was processing the response
with the intent to accept the response, but some processing error occurred. This error indicates that the response
should be resent as-is.
If other errors occur, the corresponding party may send a problem-report
to inform the other party they are abandoning the protocol.
No errors are sent in timeout situations. If the requester or responder wishes to retract the messages they sent, they record so locally and return a request_not_accepted
or response_not_accepted
error when the other party sends a request
or response
.
Error Message Example¶
{
"@type": "https://didcomm.org/didexchange/1.1/problem_report",
"@id": "5678876542345",
"~thread": { "thid": "<@id of message related to problem>" },
"~l10n": { "locale": "en"},
"problem-code": "request_not_accepted", // matches codes listed above
"explain": "Unsupported DID method for provided DID."
}
Error Message Attributes¶
- The
@type
attribute is a required string value that denotes that the received message is a problem_report within the didexchange protocol. - The
~thread
attribute provides a context for the problem, referring to the message which contains the problem. - Use of
~l10n
is encouraged, with at least locale defined for the message. - The
problem-code
attribute contains one of a fixed set of codes defined in the list above. - The
explain
attribute contains a human readable message which indicates the problem.
Flow Overview¶
- The responder gives provisional information to the requester using an explicit
invitation
message from theout-of-band
protocol or an implicit invitation in a DID the responder publishes. - In the
out-of-band
protocol, the responder is called the sender, and the requester is called the receiver. - The requester uses the provisional information to send a DID and DID Doc to the responder in a
request
message. - The responder uses sent DID Doc information to send a DID and DID Doc to the requester in a
response
message. - The requester sends the responder a
complete
message that confirms theresponse
message was received.
Implicit and Explicit Invitations¶
The DID Exchange Protocol is preceded by
- either knowledge of a resolvable DID (an implicit invitation)
- or by a out-of-band/%VER/invitation
message from the Out Of Band Protocols RFC.
The information needed to construct the request
message to start the protocol is used
- either from the resolved DID Document
- or the service
element of the handshake_protocols
attribute of the invitation
.
1. Exchange Request¶
The request
message is used to communicate the DID document of the requester to the responder using the provisional service information present in the (implicit or explicit) invitation.
The requester may provision a new DID according to the DID method spec. For a Peer DID, this involves creating a matching peer DID and key. The newly provisioned DID and DID Doc is presented in the request
message as follows:
Request Message Example¶
{
"@id": "5678876542345",
"@type": "https://didcomm.org/didexchange/1.1/request",
"~thread": {
"thid": "5678876542345",
"pthid": "<id of invitation>"
},
"label": "Bob",
"goal_code": "aries.rel.build",
"goal": "To create a relationship",
"did": "B.did@B:A",
"did_doc~attach": {
"@id": "d2ab6f2b-5646-4de3-8c02-762f553ab804",
"mime-type": "application/json",
"data": {
"base64": "eyJ0eXAiOiJKV1Qi... (bytes omitted)",
"jws": {
"header": {
"kid": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th"
},
"protected": "eyJhbGciOiJFZERTQSIsImlhdCI6MTU4Mzg4... (bytes omitted)",
"signature": "3dZWsuru7QAVFUCtTd0s7uc1peYEijx4eyt5... (bytes omitted)"
}
}
}
}
Request Message Attributes¶
- The
@type
attribute is a required string value that denotes that the received message is an exchange request. - The
~thread
decorator MUST be included: - It MUST include the ID of the parent thread (
pthid
) such that therequest
can be correlated to the corresponding (implicit or explicit)invitation
. More on correlation below. - It MAY include the
thid
property. This works according to thethid
property in the thread decorator, meaning that ifthid
is not defined it is implicitly defined as the@id
on the samerequest
message. - The
label
attribute provides a suggested label for the DID being exchanged. This allows the user to tell multiple exchange requests apart. This is not a trusted attribute. (See note onlabel
below) - The
goal_code
(optional) is a self-attested code the receiver may want to display to the user or use in automatically deciding what to do with the request message. The goal code might be used particularly when the request is sent to a resolvable DID without reference to a specfic invitation. - The goal (optional) is a self-attested string that the receiver may want to display to the user about the context-specific goal of the request message.
- The
did
attribute MUST be included. It indicates the DID being exchanged. - The
did_doc~attach
(optional), contains the DIDDoc associated with thedid
, if required. - If the
did
is resolvable (either an inlinepeer:did
or a publicly resolvable DID), thedid_doc~attach
attribute should not be included. - If the DID is a
did:peer
DID, the DIDDoc must be as outlined in RFC 0627 Static Peer DIDs.
The label
property was intended to be declared as an optional property, but was added to the RFC as a required property. If an agent wishes to not use a label in the request, an empty string (""
) or the set value Unspecified
may be used to indicate a non-value. This approach ensures existing AIP 2.0 implementations do not break.
Correlating requests to invitations¶
An invitation is presented in one of two forms:
- An explicit out-of-band invitation with its own
@id
. - An implicit invitation contained in a DID document's
service
attribute that conforms to the DIDComm conventions.
When a request
responds to an explicit invitation, its ~thread.pthid
MUST be equal to the @id
property of the invitation as described in the out-of-band RFC.
When a request
responds to an implicit invitation, its ~thread.pthid
MUST contain a DID URL that resolves to the specific service
on a DID document that contains the invitation.
Example Referencing an Explicit Invitation¶
{
"@id": "a46cdd0f-a2ca-4d12-afbf-2e78a6f1f3ef",
"@type": "https://didcomm.org/didexchange/1.1/request",
"~thread": {
"thid": "a46cdd0f-a2ca-4d12-afbf-2e78a6f1f3ef",
"pthid": "032fbd19-f6fd-48c5-9197-ba9a47040470"
},
"label": "Bob",
"goal_code": "aries.rel.build",
"goal": "To create a relationship",
"did": "B.did@B:A",
"did_doc~attach": {
"@id": "d2ab6f2b-5646-4de3-8c02-762f553ab804",
"mime-type": "application/json",
"data": {
"base64": "eyJ0eXAiOiJKV1Qi... (bytes omitted)",
"jws": {
"header": {
"kid": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th"
},
"protected": "eyJhbGciOiJFZERTQSIsImlhdCI6MTU4Mzg4... (bytes omitted)",
"signature": "3dZWsuru7QAVFUCtTd0s7uc1peYEijx4eyt5... (bytes omitted)"
}
}
}
}
Example Referencing an Implicit Invitation¶
{
"@id": "a46cdd0f-a2ca-4d12-afbf-2e78a6f1f3ef",
"@type": "https://didcomm.org/didexchange/1.1/request",
"~thread": {
"thid": "a46cdd0f-a2ca-4d12-afbf-2e78a6f1f3ef",
"pthid": "did:example:21tDAKCERh95uGgKbJNHYp#didcomm"
},
"label": "Bob",
"goal_code": "aries.rel.build",
"goal": "To create a relationship",
"did": "B.did@B:A",
"did_doc~attach": {
"@id": "d2ab6f2b-5646-4de3-8c02-762f553ab804",
"mime-type": "application/json",
"data": {
"base64": "eyJ0eXAiOiJKV1Qi... (bytes omitted)",
"jws": {
"header": {
"kid": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th"
},
"protected": "eyJhbGciOiJFZERTQSIsImlhdCI6MTU4Mzg4... (bytes omitted)",
"signature": "3dZWsuru7QAVFUCtTd0s7uc1peYEijx4eyt5... (bytes omitted)"
}
}
}
}
Request Transmission¶
The request
message is encoded according to the standards of the Encryption Envelope, using the recipientKeys
present in the invitation.
If the routingKeys
attribute was present and non-empty in the invitation, each key must be used to wrap the message in a forward request, then encoded in an Encryption Envelope. This processing is in order of the keys in the list, with the last key in the list being the one for which the serviceEndpoint
possesses the private key.
The message is then transmitted to the serviceEndpoint
.
The requester is in the request-sent
state. When received, the responder is in the request-received
state.
Request processing¶
After receiving the exchange request, the responder evaluates the provided DID and DID Doc according to the DID Method Spec.
The responder should check the information presented with the keys used in the wire-level message transmission to ensure they match.
The responder MAY look up the corresponding invitation identified in the request's ~thread.pthid
to determine whether it should accept this exchange request.
If the responder wishes to continue the exchange, they will persist the received information in their wallet. They will then either update the provisional service information to rotate the key, or provision a new DID entirely. The choice here will depend on the nature of the DID used in the invitation.
The responder will then craft an exchange response using the newly updated or provisioned information.
Request Errors¶
See Error Section above for message format details.
Request Rejected¶
Possible reasons:
- Unsupported DID method for provided DID
- Expired Invitation
- DID Doc Invalid
- Unsupported key type
- Unsupported endpoint protocol
- Missing reference to invitation
Request Processing Error¶
- unknown processing error
2. Exchange Response¶
The exchange response message is used to complete the exchange. This message is required in the flow, as it updates the provisional information presented in the invitation.
Response Message Example¶
{
"@type": "https://didcomm.org/didexchange/1.1/response",
"@id": "12345678900987654321",
"~thread": {
"thid": "<The Thread ID is the Message ID (@id) of the first message in the thread>"
},
"did": "B.did@B:A",
"did_doc~attach": {
"@id": "d2ab6f2b-5646-4de3-8c02-762f553ab804",
"mime-type": "application/json",
"data": {
"base64": "eyJ0eXAiOiJKV1Qi... (bytes omitted)",
"jws": {
"header": {
"kid": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th"
},
"protected": "eyJhbGciOiJFZERTQSIsImlhdCI6MTU4Mzg4... (bytes omitted)",
"signature": "3dZWsuru7QAVFUCtTd0s7uc1peYEijx4eyt5... (bytes omitted)"
}
}
},
"did_rotate~attach": {
"mime-type": "text/string",
"data": {
"base64": "Qi5kaWRAQjpB",
"jws": {
"header": {
"kid": "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th"
},
"protected": "eyJhbGciOiJFZERTQSIsImlhdCI6MTU4Mzg4... (bytes omitted)",
"signature": "3dZWsuru7QAVFUCtTd0s7uc1peYEijx4eyt5... (bytes omitted)"
}
}
}
}
The invitation's recipientKeys
should be dedicated to envelopes authenticated encryption throughout the exchange. These keys are usually defined in the KeyAgreement
DID verification relationship.
Response Message Attributes¶
- The
@type
attribute is a required string value that denotes that the received message is an exchange request. - The
~thread
decorator MUST be included. It contains athid
reference to the@id
of the request message. - The
did
attribute MUST be included. It denotes the DID in use by the responder. Note that this MAY NOT be the same DID used in the invitation. - The
did_doc~attach
optional, contains the DID Doc associated with thedid
, if required. - If the
did
is resolvable (either an inlinedid:peer
or a publicly resolvable DID), thedid_doc~attach
attribute should not be included. - If the DID is a
did:peer
identifier, the DIDDoc must be as outlined in RFC 0627 Static Peer DIDs. - The
did_rotate~attach
attribute is optional, but SHOULD be included if thedid
attribute is resolvable and thedid_doc~attach
is not included. The value is the Base64url encoded DID, and signed with the key used in the invitation.
In addition to a new DID, the associated DID Doc might contain a new endpoint. This new DID and endpoint are to be used going forward in the relationship.
Response Transmission¶
The message should be packaged in the encrypted envelope format, using the keys from the request, and the new keys presented in the internal did doc.
When the message is sent, the responder are now in the response-sent
state. On receipt, the requester is in the response-received
state.
Response Processing¶
When the requester receives the response
message, they will decrypt the authenticated envelope which confirms the source's authenticity. After decryption validation, the signature on the did_doc~attach
or did_rotate~attach
MUST be validated, if present. The key used in the signature MUST match the key used in the invitation. After attachment signature validation, they will update their wallet with the new information, and use that information in sending the complete
message.
Response Errors¶
See Error Section above for message format details.
Response Rejected¶
Possible reasons:
- unsupported DID method for provided DID
- Expired Request
- DID Doc Invalid
- Unsupported key type
- Unsupported endpoint protocol
- Invalid Signature
Response Processing Error¶
- unknown processing error
3. Exchange Complete¶
The exchange complete
message is used to confirm the exchange to the responder. This message is required in the flow, as it marks the exchange complete. The responder may then invoke any protocols desired based on the context expressed via the pthid
in the DID Exchange protocol.
Complete Message Example¶
{
"@type": "https://didcomm.org/didexchange/1.1/complete",
"@id": "12345678900987654321",
"~thread": {
"thid": "<The Thread ID is the Message ID (@id) of the first message in the thread>",
"pthid": "<pthid used in request message>"
}
}
The pthid
is required in this message, and must be identical to the pthid
used in the request
message.
After a complete
message is sent, the requester is in the completed
terminal state. Receipt of the message puts the responder into the completed
state.
Complete Errors¶
See Error Section above for message format details.
Complete Rejected¶
This is unlikely to occur with other than an unknown processing error (covered below), so no possible reasons are listed. As experience is gained with the protocol, possible reasons may be added.
Complete Processing Error¶
- unknown processing error
Next Steps¶
The exchange between the requester and the responder has been completed. This relationship has no trust associated with it. The next step should be to increase the trust to a sufficient level for the purpose of the relationship, such as through an exchange of proofs.
Peer DID Maintenance¶
When Peer DIDs are used in an exchange, it is likely that both the requester and responder will want to perform some relationship maintenance such as key rotations. Future RFC updates will add these maintenance ../../features.
Reference¶
- https://docs.google.com/document/d/1mRLPOK4VmU9YYdxHJSxgqBp19gNh3fT7Qk4Q069VPY8/edit#heading=h.7sxkr7hbou5i
- Agent to Agent Communication Video
- Agent to Agent Communication Presentation
- Problem_report message adopted into message family, following form defined by the Problem Report HIPE
- RFC0519 Goal Codes
Drawbacks¶
N/A at this time
Prior art¶
- This process is similar to other key exchange protocols.
Unresolved questions¶
- N/A at this time
Implementations¶
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 |
---|---|
Trinsic.id | Commercial mobile and web app built using Aries Framework - .NET MISSING test results |