§ DIDComm Messaging v2.0
Specification Status: DIF-Ratified Specification
Latest Stable version: identity.foundation/didcomm-messaging/spec/v2.1
Editors: Sam Curren (Indicio), Tobias Looker (Mattr), Oliver Terbu (ConsenSys)
Contributors: Sam Curren (Indicio), Tobias Looker (MATTR), Oliver Terbu (ConsenSys), Kyle Den Hartog (MATTR), Baha Shaaban (SecureKey), Drummond Reed (Evernym), Steve McCown (Anonyome Labs), Troy Ronda (SecureKey), George Aristy (SecureKey), Vyacheslav Gudkov (DSR), Alexander Shcherbakov (DSR), Alexander Martynov (DSR), Daniel Buchner (Microsoft), Devin Fisher (Evernym), Orie Steele (Transmute), Brian Richter (Aviary Tech), Juan Caballero (Centre.io), @liormargalit, Timo Glastra (Animo Solutions), Andrew Whitehead (Government of British Columbia), Nader Helmy (MATTR), Markus Sabadello (Danube Tech), Patrick McClurg (SICPA), Stephen Curran (Government of British Columbia), Daniel Hardman (SICPA), Moe Jangda (Block - TBD)
- Participate:
- GitHub repo
- File a bug
- Commit history
§ Purpose and Scope
The purpose of DIDComm Messaging is to provide a secure, private communication methodology built atop the decentralized design of DIDs.
It is the second half of this sentence, not the first, that makes DIDComm interesting. “Methodology” implies more than just a mechanism for individual messages, or even for a sequence of them. DIDComm Messaging defines how messages compose into the larger primitive of application-level protocols and workflows, while seamlessly retaining trust. “Built atop … DIDs” emphasizes DIDComm’s connection to the larger decentralized identity movement, with its many attendent virtues.
Of course, robust mechanisms for secure communication already exist. However, most rely on key registries, identity providers, certificate authorities, browser or app vendors, or similar centralizations. Many are for unstructured rich chat only — or enable value-add behaviors through proprietary extensions. Many also assume a single transport, making it difficult to use the same solution for human and machine conversations, online and offline, simplex and duplex, across a broad set of modalities. And because these limitations constantly matter, composability is limited — every pairing of human and machine for new purposes requires a new endpoint, a new API, and new trust. Workflows that span these boundaries are rare and difficult.
All of these factors perpetuate an asymmetry between institutions and ordinary people. The former maintain certificates and always-connected servers, and publish APIs under terms and conditions they dictate; the latter suffer with usernames and passwords, poor interoperability, and a Hobson’s choice between privacy and convenience.
DIDComm Messaging can fix these problems. Using DIDComm, individuals on semi-connected mobile devices become full peers of highly available web servers operated by IT experts. Registration is self-service, intermediaries require little trust, and terms and conditions can come from any party.
DIDComm Messaging enables higher-order protocols that inherit its security, privacy, decentralization, and transport independence. Examples include exchanging verifiable credentials, creating and maintaining relationships, buying and selling, scheduling events, negotiating contracts, voting, presenting tickets for travel, applying to employers or schools or banks, arranging healthcare, and playing games. Like web services atop HTTP, the possibilities are endless; unlike web services atop HTTP, many parties can participate without being clients of a central server, and they can use a mixture of connectivity models and technologies. And these protocols are composable into higher-order workflows without constantly reinventing the way trust and identity transfer across boundaries.
§ Overview
To understand how DIDComm Messaging works, consider a situation where Alice wants to negotiate with Bob to sell something online. Because DIDComm, not direct human communication, is the methodology in this example, Alice’s software agent and Bob’s software agent are going to exchange a series of messages.
Alice may just press a button and be unaware of details, but underneath, her agent begins by preparing a plaintext JSON message about the proposed sale. The particulars of selling are irrelevant here, but would be described in the spec for a higher-level “sell something” protocol that uses DIDComm Messaging as its foundation. Alice’s agent then gets two key pieces of information from Bob, typically by resolving Bob’s DID Doc:
- An endpoint (web, email, etc) where messages can be delivered to Bob.
- The public key that Bob’s agent is using in the Alice:Bob relationship.
Now Alice’s agent uses Bob’s public key to encrypt the plaintext so that only Bob’s agent can read it, adding authentication with its own private key. The agent arranges delivery to Bob. This “arranging” can involve various hops and intermediaries. It can be complex. (See Routing in the DIDComm Guidebook.)
Bob’s agent eventually receives and decrypts the message, authenticating its sender as Alice using her public key. It looks up this key in Alice’s DID doc, and captures an endpoint for her at the same time. Bob’s agent then prepares its response and routes it back using a reciprocal process (plaintext → encrypt with authentication → arrange delivery).
That’s the essence, in the most common scenarios. However, it does not fit all DIDComm Messaging interactions:
- DIDComm doesn’t always involve turn-taking and request-response.
- DIDComm interactions can involve more than 2 parties, and the parties are not always individuals.
- DIDComm may include formats other than JSON.
Before we provide more details, let’s explore what drives the design of DIDComm Messaging.
§ Specific Requirements
The DIDComm Messaging design attempts to be:
- Secure (specifically, MUST preserve the integrity of messages against tampering; MUST allow the authenticity of messages and message senders to be proved; MUST use best-of-breed crypto; MUST allow parties to emit both repudiable and non-repudiable messages; perfect forward secrecy is not formally required due to the lack of a session construct, but similar outcomes must be achievable, as described in the Guidebook)
- Private (MUST prevent unauthorized third parties from learning who’s communicating about what, when; MUST give the sender the option to be anonymous to the recipient)
- Decentralized (derives trust for encryption, signing, authn, and authz from control of decentralized identifiers rather than oracles like CAs, IDPs, etc; usable at the edge)
- Transport-agnostic (usable over HTTPS 1.x and 2.0, WebSockets, BlueTooth, chat, push notifications, libp2p, AMQP, SMTP, NFC, sneakernet, snail mail; supports both simplex and duplex; works offline; doesn’t assume client-server or synchronous or real-time; allows paired or n-wise or public broadcast usage)
- Routable (like email, Alice can talk to Bob without a direct connection to Bob; allows mixed and dynamic transports; passes through mix networks and other generic infrastructure that sees only payload BLOBs)
- Interoperable (works across programming languages, blockchains, vendors, OS/platforms, networks, legal jurisdictions, geos, cryptographic schemes, and hardware — as well as across time; avoids vendor lock-in)
- Extensible (lets developers start simple without heavy learning or dependencies; customizes easily; facilitates higher-level protocols that inherit DIDComm Messaging’s guarantees)
- Efficient (doesn’t waste bandwidth, battery, storage space, or CPU)
§ Ramifications
As a list of buzz words, this may elicit nods rather than surprise. However, design tradeoffs are inevitable, and several of these items have noteworthy ramifications.
§ Message-Based, Asynchronous, and Simplex
The dominant paradigm in mobile and web development today is duplex request-response. You call an API with certain inputs, and you wait to get back a response with certain outputs over the same channel, shortly thereafter. This is the world of OpenAPI/Swagger (and most RPC, for that matter), and it has many virtues.
Unfortunately, many agents are not good analogs to web servers. They may be mobile devices that turn off at unpredictable intervals and that lack a stable connection to the network. They may need to work peer-to-peer, when the internet is not available. They may need to interact in time frames of hours or days, not with 30-second timeouts. They may not listen over the same channel that they use to talk.
Because of this, the fundamental paradigm for DIDComm Messaging is message-based, asynchronous, and simplex. Alice’s agent sends a message over channel A. Sometime later, it may receive a message (which may or may not be a response) from Bob’s agent over channel B. This is much closer to an email paradigm than a web paradigm.
On top of this foundation, it is possible to build elegant, synchronous request-response interactions. All of us have interacted with a friend who’s emailing or texting us in near-realtime. However, interoperability begins with a least-common-denominator assumption that’s simpler.
§ Message-Level Security, Reciprocal Authentication
The security and privacy goals, and the asynchronous+simplex design decision, break familiar web assumptions in another way. Servers are commonly run by institutions, and we authenticate them with certificates. People and things are usually authenticated to servers by some sort of login process quite different from certificates, and this authentication is cached in a session object that expires. Furthermore, web security is provided at the transport level (TLS); it is not an independent attribute of the messages themselves.
In a partially disconnected world where a communication channel is not assumed to support duplex request-response, and where the security can’t be ignored as a transport problem, traditional TLS, login, and expiring sessions are impractical. Furthermore, centralized servers and certificate authorities perpetuate a power and UX imbalance between servers and clients that doesn’t fit with peer-oriented DIDComm Messaging.
DIDComm Messaging uses public key cryptography, not certificates from some parties and passwords from others. Its security guarantees are independent of the transport over which it flows. It is sessionless (though sessions can easily be built atop it). When authentication is required, all parties do it the same way.
§ Message Formats
This specification discusses messages in three different formats. The casual phrase “DIDComm message” is ambiguous, but usually refers to DIDComm Encrypted Messages (the outermost box in the diagram below). These will constitute the vast majority of network traffic in most DIDComm Messaging deployments, and are responsible for security guarantees in the system. However, the role of encrypted messages cannot be understood without reference to the simpler formats they contain, so all three formats are explored below.
§ IANA Media Types
Circumstances sometimes require communication about the format of DIDComm messages. The canonical way to do this is with IANA media types, based on the conventions of RFC6838.
All three DIDComm message formats — plaintext, signed, and encrypted — can be correctly understood as more generic JWMs (JSON Web Messages) or even as arbitrary JOSE content. Since code that expects JOSE conventions but not DIDComm may matter in some implementations, this spec recommends the JOSE convention of using typ
to make JOSE structure formats self-describing. This is particularly helpful in the outermost envelope of any DIDComm message, before unwrapping begins. (As RFC 7515 notes, typ
“will typically not be used by applications when the kind of object is already known.”)
When a sender prepares a message for routing, it wraps the message once for each hop that has cryptographic consequences in the path. Each wrapping operation applies one or more layers of envelope, and potentially transforms the associated media type of the output. The relevant possibilities are:
Envelopes | IANA type (typ header) |
Notes |
---|---|---|
plaintext (no envelope) | application/didcomm-plain+json |
Used as the building block of higher-level protocols, but rarely transmitted directly, since it lacks security guarantees. |
signed(plaintext) | application/didcomm-signed+json |
Adds non-repudiation to a plaintext message; whoever receives a message wrapped in this way can prove its origin to any external party. |
anoncrypt(plaintext) | application/didcomm-encrypted+json |
Guarantees confidentiality and integrity without revealing the identity of the sender. |
authcrypt(plaintext) | application/didcomm-encrypted+json |
Guarantees confidentiality and integrity. Also proves the identity of the sender – but in a way that only the recipient can verify. This is the default wrapping choice, and SHOULD be used unless a different goal is clearly identified. By design, this combination and all other combinations that use encryption in their outermost layer share an identical IANA media type, because only the recipient should care about the difference. |
anoncrypt(sign(plaintext)) | application/didcomm-encrypted+json |
Guarantees confidentiality, integrity, and non-repudiation – but prevents an observer of the outer envelope from accessing the signature. Relative to authcrypt(plaintext), this increases guarantees to the recipient, since non-repudiation is stronger than simple authentication. However, it also forces the sender to talk “on the record” and is thus not assumed to be desirable by default. |
authcrypt(sign(plaintext)) | application/didcomm-encrypted+json |
Adds no useful guarantees over the previous choice, and is slightly more expensive, so this wrapping combination SHOULD NOT be emitted by conforming implementations. However, implementations MAY accept it. If they choose to do so, they MUST emit an error if the signer of the plaintext is different from the sender identified by the authcrypt layer. |
anoncrypt(authcrypt(plaintext)) | application/didcomm-encrypted+json |
A specialized combination that hides the skid header in the authcrypt envelope, so the hop immediately sourceward of a mediator cannot discover an identifier for the sender. See Protecting the Sender Identity. |
In the aggregate, complex combinations of envelopes may occur across a route. However, in the set of envelopes that targets a single hop, envelope combinations other than the ones above MUST NOT be used. In particular, it makes no sense to use anoncrypt(authcrypt(sign(plaintext))); use anoncrypt(sign(plaintext)) instead.
Aligning with RFC 7515, IANA types for DIDComm messages MAY omit the application/
prefix; the recipient MUST treat media types not containing /
as having the application/
prefix present.
§ DIDComm Plaintext Messages
A DIDComm message in its plaintext form, not packaged into any protective envelope, is known as a DIDComm plaintext message.
When higher-level protocols are built atop DIDComm Messaging, applications remove protective envelopes and process the plaintext that’s inside. They think about protective envelopes the way webapps think about TLS: as required background context, not as a focus. Thus, application-level constructs are embodied in features of plaintext messages, and specifications for higher-level protocols typically document message structure and provide examples in this format.
In isolation, plaintext messages lack confidentiality and integrity guarantees, and are repudiable. They are therefore not normally transported across security boundaries. However, this may be a helpful format to inspect in debuggers, since it exposes underlying semantics, and it is the format used in this specification to give examples of headers and other internals. Depending on ambient security, plaintext may or may not be an appropriate format for DIDComm Messaging data at rest.
The media type for a generic DIDComm plaintext message MUST be reported as application/didcomm-plain+json
by conformant implementations.
The media type of the envelope MAY be set in the typ
property of the plaintext; it SHOULD be set if the message is intended for use without a signature or encryption.
When persisted as a file or attached as a payload in other contexts, the file extension for DIDComm plaintext messages SHOULD be dcpm
, giving a globbing pattern of *.dcpm
; this SHOULD be read as “Star Dot D C P M” or as “D C P M” files. We imagine people will reference this media type by saying, “I am looking at a DIDComm Plaintext Message file”, or “This database record is in DIDComm Plaintext format”, or “Does my editor have a DIDComm Plaintext Message plugin?” A possible icon for this file format depicts green JSON text in a message bubble (svg | 256x256 | 128x128 | 64x64):
§ DIDComm Signed Messages
A DIDComm signed message is a signed JWM envelope that associates a non-repudiable signature with the plaintext message inside it.
Signed messages are not necessary to provide message integrity (tamper evidence), or to prove the sender to the recipient. Both of these guarantees automatically occur with the authenticated encryption in DIDComm encrypted messages. Signed messages are only necessary when the origin of plaintext has to be provable to third parties, or when the sender can’t be proven to the recipient by authenticated encryption because the recipient is not known in advance (e.g., in a broadcast scenario). Adding a signature when one is not needed can degrade rather than enhance security because it relinquishes the sender’s ability to speak off the record. We therefore expect signed messages to be used in a few cases, but not as a matter of course.
When a message is both signed and encrypted, this spec echoes the JOSE recommendation about how to combine: sign the plaintext first, and then encrypt. (The opposite order would imply that the signer committed to opaque data. This would be less safe, and would undermine non-repudiation.)
The media type of a DIDComm signed message MUST be application/didcomm-signed+json
.
The media type of the envelope SHOULD be set in the typ
property of the JWS.
In order to avoid surreptitious forwarding or malicious usage of a signed message, a signed message SHOULD contain a properly defined to
header. In the case where a message is both signed and encrypted, the inner (signed) JWM being signed MUST contain a to
header.
When persisted as a file or attached as a payload in other contexts, the file extension for DIDComm signed messages SHOULD be dcsm
, giving a globbing pattern of *.dcsm
; this SHOULD be be read as “Star Dot D C S M” or as “D C S M” files. A possible icon for this media type depicts a signed envelope (svg | 256x256 | 128x128 | 64x64):
§ DIDComm Encrypted Messages
A DIDComm encrypted message is an encrypted JWM. It hides its content from all but authorized recipients, discloses and proves the sender to exactly and only those recipients, and provides integrity guarantees. It is important in privacy-preserving routing. It is what normally moves over network transports in DIDComm Messaging applications, and is the safest format for storing DIDComm Messaging data at rest.
The media type of a non-nested DIDComm encrypted message MUST be application/didcomm-encrypted+json
.
Note: If future versions of this spec allow binary encodings, variations like
application/didcomm-encrypted+cbor
(see CBOR RFC 7049, section 7.5),application/didcomm-encrypted+msgpack
, orapplication/didcomm-encrypted+protobuf
may become reasonable. In the future, specifications that encompass communications patterns other than messaging — DIDComm Multicast or DIDComm Streaming, for example — might use a suffix:application/didcomm-encrypted-multicast
or similar.
The media type of the envelope SHOULD be set in the typ
property of the JWE.
When persisted as a file or attached as a payload in other contexts, the file extension for DIDComm encrypted messages SHOULD be dcem
, giving a globbing pattern of *.dcem
; this SHOULD be read as “Star Dot D C E M” or as “D C E M” files. A possible icon for this file format depicts an envelope with binary overlay, protected by a lock (svg | 256x256 | 128x128 | 64x64):
§ Plaintext Message Structure
As mentioned above, DIDComm plaintext messages are based on JWM. JWMs follow the same general pattern as other JOSE containers, but are optimized for larger and more arbitrary structure than simple tokens.
A plaintext message has an outermost attribute, type
, that identifies the application-level message category to which it belongs. This value of type
is a specialized URI; it allows messages to be mapped to specific handler code. Other outermost attributes include a message’s id
and its media type (typ
attribute, for generic JWM handling, as described above). In addition, plaintext messages may have other other attributes that have meaning across many message types. Such attributes at the top level of a message are called headers.
A plaintext message also includes attributes and data specific to its message type. These are contained within its body
attribute.
Prior to being sent, plaintext is usually encrypted into a JWE according to the JWM specification.
The following example shows common elements of a DIDComm plaintext message.
{
"id": "1234567890",
"type": "<message-type-uri>",
"from": "did:example:alice",
"to": ["did:example:bob"],
"created_time": 1516269022,
"expires_time": 1516385931,
"body": {
"message_type_specific_attribute": "and its value",
"another_attribute": "and its value"
}
}
§ Message Headers
A DIDComm plaintext message conveys most of its application-level data inside a JSON body
object that is a direct child of the message root. The structure inside body
is predicted by the value of the message’s type
attribute, and varies according to the definition of the protocol-specific message in question. Each type
of message has its own schema for body
.
However, some attributes are common to many different message types. When metadata about a message means the same thing regardless of context, and when it is susceptible to generic rather than message-specific handling, that metadata can be placed in headers. Headers are siblings of body
and may be added to any message type. They are encrypted and decrypted (and/or signed and verified) along with body
and therefore have an identical audience.
If headers need to be sent and there is no message to attach them to, an empty message may be sent.
Headers in DIDComm Messaging are intended to be extensible in much the same way that headers in HTTP or SMTP are extensible. A few headers are predefined:
-
id
- REQUIRED. Message ID. Theid
attribute value MUST be unique to the sender, across all messages they send. See Threading > Message IDs for constraints on this value. -
type
- REQUIRED. A URI that associates thebody
of a plaintext message with a published and versioned schema. Useful for message handling in application-level protocols. Thetype
attribute value MUST be a valid message type URI, that when resolved gives human readable information about the message category. -
to
- OPTIONAL. Identifier(s) for recipients. MUST be an array of strings where each element is a valid DID or DID URL (without the fragment component) that identifies a member of the message’s intended audience. These values are useful for recipients to know which of their keys can be used for decryption. It is not possible for one recipient to verify that the message was sent to a different recipient.When Alice sends the same plaintext message to Bob and Carol, it is by inspecting this header that the recipients learn the message was sent to both of them. If the header is omitted, each recipient SHOULD assume they are the only recipient (much like an email sent only to
BCC:
addresses).For signed messages, there are specific requirements around properly defining the
to
header outlined in the DIDComm Signed Message definition above. This prevents certain kind of forwarding attacks, where a message that was not meant for a given recipient is forwarded along with its signature to a recipient which then might blindly trust it because of the signature.Upon reception of a message with a defined
to
header, the recipient SHOULD verify that their own identifier appears in the list. Implementations MUST NOT fail to accept a message when this is not the case, but SHOULD give a warning to their user as it could indicate malicious intent from the sender.The
to
header cannot be used for routing, since it is encrypted at every intermediate point in a route. Instead, theforward
message contains anext
attribute in its body that specifies the target for the next routing operation. -
from
- OPTIONAL when the message is to be encrypted via anoncrypt; REQUIRED when the message is encrypted via authcrypt. Sender identifier. Thefrom
attribute MUST be a string that is a valid DID or DID URL (without the fragment component) which identifies the sender of the message. When a message is encrypted, the sender key MUST be authorized for encryption by this DID. Authorization of the encryption key for this DID MUST be verified by message recipient with the proper proof purposes. When the sender wishes to be anonymous using authcrypt, it is recommended to use a new DID created for the purpose to avoid correlation with any other behavior or identity. Peer DIDs are lightweight and require no ledger writes, and therefore a good method to use for this purpose. -
thid
- OPTIONAL. Thread identifier. Uniquely identifies the thread that the message belongs to. If not included, theid
property of the message MUST be treated as the value of thethid
. See Threads for details. -
pthid
- OPTIONAL. Parent thread identifier. If the message is a child of a thread thepthid
will uniquely identify which thread is the parent. See Parent Threads for details. -
created_time
- OPTIONAL but recommended. Message Created Time. This attribute is used for the sender to express when they created the message, expressed in UTC Epoch Seconds (seconds since 1970-01-01T00:00:00Z) as an integer. This allows the recipient to guess about transport latency and clock divergence. The difference between when a message is created and when it is sent is assumed to be negligible; this lets timeout logic start from this value. -
expires_time
- OPTIONAL. Message Expires Time. This attribute is used for the sender to express when they will consider the message to be expired, expressed in UTC Epoch Seconds (seconds since 1970-01-01T00:00:00Z) as an integer. By default, the meaning of “expired” is that the sender will abort the protocol if it doesn’t get a response by this time. However, protocols can nuance this in their formal spec. For example, an online auction protocol might specify that timed out bids must be ignored instead of triggering a cancellation of the whole auction. When omitted from any given message, the message is considered to have no expiration by the sender. -
body
- REQUIRED. Thebody
attribute contains all the data and structure defined uniquely for the schema associated with thetype
attribute. This attribute MUST be present, even if empty. It MUST be a JSON object conforming to RFC 7159. -
attachments
- OPTIONAL. See Attachments for detail.
With respect to headers, DIDComm Messaging follows the extensibility pattern established by the JW* family of standards. A modest inventory of predefined “header” fields is specified, as shown above. Additional fields with unreserved names can be added at the discretion of producers and consumers of messages; any software that doesn’t understand such fields SHOULD ignore them and MUST NOT fail because of their inclusion in a message.
Aligning with RFC 6648, DIDComm Messaging explicitly rejects the X-*
headers convention that creates divergent pseudo-standards; if a new header needs broad support, proper standardization is required. Since we expect header fields to be small in number and modest in complexity, we expect this sort of powerful extensibility to be unnecessary in most cases.
§ Simple vs. Structured
Headers can be simple (mapping a header name to an integer or a string) or structured (mapping a header name to JSON substructure – an array or JSON object). When defining a new header type, the following guidelines apply:
- Headers SHOULD NOT use more structure than necessary; simple headers are preferred.
- However, a header value SHOULD NOT require interpretation over and above ordinary JSON parsing. Prefer JSON structure to specialized string DSLs like the one that encodes media type preferences in an HTTP
Accept
header. (HTTP Structured Headers provide similar functionality but are unnecessary here, since plaintext messages already have an easily parseable syntax.) - Headers that are only meaningful together SHOULD be grouped into a JSON object.
§ Message Layer Addressing Consistency
When messages are combined into layers as shown above in the Media Types table, various attributes must be checked for consistency by the message recipient.
- The
from
attribute in the plaintext message MUST match theskid
attribute in the encryption layer. - The
to
attribute in the plaintext message MUST contain thekid
attribute of an encrypted message. - The
from
attribute in the plaintext message MUST match the signer’skid
in a signed message.
When one of these checks fails, the result MUST be an error so clients know that the trust choices in the message packaging are inconsistent.
§ Attachments
It is common for DIDComm messages to supplement formalized structure with arbitrary data — images, documents, or types of media not yet invented. Such content is “attached” to DIDComm messages in much the same way that attachments work in email.
Attachments are contained within a list in the attachments
header.
Each attachment is described with an instance of a JSON object that has the following structure.
id
- OPTIONAL but recommended. Identifies attached content within the scope of a given message, so it can be referenced. For example, in a message documenting items for sale on an auction website, there might be a field namedfront_view
that contains the value#attachment1
; this would reference an attachment to the message withid
equal toattachment1
. If omitted, then there is no way to refer to the attachment later in the thread, in error messages, and so forth. Because theid
of an attachment is used to compose URIs, this value should be brief and MUST consist entirely of unreserved URI characters – meaning that it is not necessary to percent encode the value to incorporate it in a URI.description
- OPTIONAL. A human-readable description of the content.filename
- OPTIONAL. A hint about the name that might be used if this attachment is persisted as a file. It need not be unique. If this field is present andmedia_type
is not, the extension on the filename may be used to infer a MIME type.media_type
- OPTIONAL. Describes the media type of the attached content.format
- OPTIONAL. Further describes the format of the attachment if themedia_type
is not sufficient.lastmod_time
- OPTIONAL. A hint about when the content in this attachment was last modified.data
: A JSON object that gives access to the actual content of the attachment. This MUST contain at least one of the following subfields, and enough of them to allow access to the data:jws
- OPTIONAL. A JWS in detached content mode, where thepayload
field of the JWS maps tobase64
or to something fetchable vialinks
. This allows attachments to be signed. The signature need not come from the author of the message.hash
- OPTIONAL. The hash of the content encoded in multi-hash format. Used as an integrity check for the attachment, and MUST be used if the data is referenced via thelinks
data attribute.links
- OPTIONAL. A list of zero or more locations at which the content may be fetched. This allows content to be attached by reference instead of by value.base64
- OPTIONAL. Base64url-encoded data, when representing arbitrary content inline instead of vialinks
.json
- OPTIONAL. Directly embedded JSON data, when representing content inline instead of vialinks
, and when the content is natively conveyable as JSON.
byte_count
- OPTIONAL. mostly relevant when content is included by reference instead of by value. Lets the receiver guess how expensive it will be, in time, bandwidth, and storage, to fully fetch the attachment.
§ Attachment Example
{
"type": "<sometype>",
"to": [
"did:example:mediator"
],
"body": {
"attachment_id": "1",
"encrypted_details": {
"id": "x",
"encrypted_to": "",
"other_details": "about attachment"
}
},
"attachments": [
{
"id": "1",
"description": "example b64 encoded attachment",
"data": {
"base64": "WW91ciBob3ZlcmNyYWZ0IGlzIGZ1bGwgb2YgZWVscw=="
}
},
{
"id": "2",
"description": "example linked attachment",
"data": {
"hash": "<multi-hash>",
"links": [
"https://path/to/resource"
]
}
},
{
"id": "x",
"description": "example encrypted DIDComm message as attachment",
"media_type": "application/didcomm-encrypted+json",
"data": {
"json": {
//jwe json structure
}
}
}
]
}
§ Goal Codes
Goal codes are used to coordinate the purpose of an interaction. Some protocols are generic enough to be used for different purposes; goal codes communicate the purpose of the interaction unambiguously. Out of Band messages provide an example: A proposes to B that they connect, and supplies a goal code to clarify why the connection is desired. Goal codes may also be used to signal intent to engage in a sequence of protocols as a unit. This is useful for interoperability profiles.
Goal codes are a structured string datatype that is used in plaintext message content. This spec defines their meaning but not their location or name in a message’s structure; the latter questions are for specific protocols to decide.
Reading left to right, goal codes strings convey meaning that gets more specific as the string progresses. In order to avoid collision between different efforts and goal codes, goal codes defined outside of this spec MUST use Reverse Domain Name Notation with the associated effort’s domain as a prefix: com.example.category.specific
. Any structure after the domain name portion is acceptable; DIDComm v1 proposed some conventions that may be useful.
§ Negotiating Compatibility
When parties want to communicate via DIDComm, a number of mechanisms must align. These include:
- The type of service endpoint used by each party
- The key types used for encryption and/or signing
- The format of the encryption and/or signing envelopes
- The encoding of plaintext messages
- The protocol used to forward and route
- The protocol embodied in the plaintext messages
Although DIDComm allows flexibility in each of these choices, it is not expected that a given DIDComm implementation will support many permutations. Rather, we expect a few sets of choices that commonly go together. We call a set of choices that work well together a profile. Profiles are identified by a string that matches the conventions of IANA media types, but they express choices about plaintext, encryption, signing, and routing in a single value. The following profile identifiers are defined in this version of the spec:
§ Defined Profiles
didcomm/aip1
: The encryption envelope, signing mechanism, plaintext conventions, and routing algorithms embodied in Aries AIP 1.0, circa 2020.didcomm/aip2;env=rfc19
: The signing mechanism, plaintext conventions, and routing algorithms embodied in Aries AIP 2.0, circa 2021 — with the old-style encryption envelope from Aries RFC 0019. This legal variant of AIP 2.0 minimizes differences with codebases that shipped AIP 1.0 support.didcomm/aip2;env=rfc587
: The signing mechanism, plaintext conventions, and routing algorithms embodied in Aries AIP 2.0, circa 2021 — with the new-style encryption envelope from Aries RFC 0587. This legal variant of AIP 2.0 lays the foundation for DIDComm v2 support by anticipating the eventual envelope change.didcomm/v2
: The encryption envelope, signing mechanism, plaintext conventions, and routing algorithms embodied in this spec.
Profiles are named in the accept
section of a DIDComm service endpoint and in an out-of-band message. When Alice declares that she accepts didcomm/v2
, she is making a declaration about more than her own endpoint. She is saying that all publicly visible steps in an inbound route to her will use the didcomm/v2
profile, such that a sender only has to use didcomm/v2
choices to get the message from Alice’s outermost mediator to Alice’s edge. It is up to Alice to select and configure mediators and internal routing in such a way that this is true for the sender.
§ Incompatible Profiles
When two parties attempt to communicate but discover that they are using incompatible profiles, each SHOULD attempt to use a profile that both support. This could require a more advanced peer to fall back, or a less advanced peer to move forward. There is no negotiation mechanism for this, since it would create a recursive versioning problem of its own. Ideally, a failure to find compatibility would trigger a problem report that uses the conventions of whatever party will receive it. However, this may be difficult for software that intentionally avoids dependencies. Hard-coding a tiny slice of functionality from another DIDComm profile, just to receive or emit problem reports from incompatible parties, is a possible solution.
§ Security
§ Message Encryption
DIDComm Messages are encrypted with the keys of a single DID. A message being sent to multiple DIDs MUST be encrypted for each DID independently. If a single DID has multiple key types, the keys of each type must be used in a separate encryption of the message.
DIDComm supports two types of message encryption: Authenticated Sender Encryption (“authcrypt”) and Anonymous Sender Encryption (“anoncrypt”). Both forms are encrypted to the recipient DID. Only authcrypt provides direct assurances of who the sender is. Each encrypted message MUST use either authcrypt or anoncrypt.
The encrypted form of a JWM is a JWE in General JSON Format. The JOSE family defines JSON Web Algorithms (JWAs) which standardize certain cryptographic operations that are related to preparing JOSE structures. For the purposes of interoperability, DIDComm messaging does not support all JWAs; rather, it takes a subset of the supported algorithms that are applicable for the following cases around secure messaging. These supported algorithms are listed in tables later in the spec.
§ Sender Authenticated Encryption
For an encrypted DIDComm message, the JWA of ECDH-1PU MUST be used within the structure of a JWE.
§ Anonymous Encryption
When a sender would like to encrypt a message for a recipient DID or multiple recipient DIDs but not be authenticated by the recipients, the JWA of ECDH-ES
defined by RFC 7518 SHOULD be used within the structure of a JWE.
Anonymous encryption removes authentication of the sender, which is a significant benefit of DIDComm Messaging; it may make sense, but should only be done thoughtfully, when the context of the use case justifies it. Pairing anonymous encryption with a method of message authentication other than authcrypt as defined in this specification is not generally recommended, as it may have unintended side effects. In particular, a signed message that is anonymously encrypted accomplishes authentication, but loses the repudiability of authcrypt. Further discussion of message authentication can be found in the DIDComm Guidebook.
§ Curves and Content Encryption Algorithms
For the keys involved in key agreement, the first three elliptic curves in this table MUST be supported, and P-521 is optional.
Curve | Description |
---|---|
X25519 | The underlying curve is actually Curve25519 , however when used in the context of Diffie-Hellman the identifier of X25519 is used |
P-384 | NIST defined P-384 elliptic curve |
P-256 | NIST defined P-256 elliptic curve - deprecated in favor of P-384 |
P-521 | NIST defined P-521 elliptic curve. Optional. |
For content encryption of the message, DIDComm inherits the implementation definitions from JSON Web Algorithms for AES 256-bit keys. In addition, DIDComm defines optional implementation usage of the draft XC20P algorithm.
To prevent invalid curve and weak point attacks, implementations that decrypt messages from a NIST curve MUST verify that the received public key (contained in the JWE protected header) is on the curve in question. This check may already be done by some JOSE libraries, but developers should not assume this is the case. See this explanation of the risk, and this practical guide for how to perform the verification correctly.
Algorithm(JWA) | Description | Authcrypt/Anoncrypt | Requirements |
---|---|---|---|
A256CBC-HS512 | AES256-CBC + HMAC-SHA512 with a 512 bit key | Authcrypt/Anoncrypt | Required |
A256GCM | AES256-GCM with a 256 bit key | Anoncrypt | Recommended |
XC20P | XChaCha20Poly1305 with a 256 bit key | Anoncrypt | Optional |
Implementations MUST choose nonces securely.
§ Key Wrapping Algorithms
KW Algorithm | Curve (epk crv) | key type (epk kty) | Description |
---|---|---|---|
ECDH-ES+A256KW | P-256 | EC | ECDH-ES key wrapping using key with NIST defined P-256 elliptic curve to create a 256 bits key as defined in RFC 7518 |
ECDH-ES+A256KW | P-384 | EC | ECDH-ES key wrapping using key with NIST defined P-384 elliptic curve to create a 256 bits key as defined in RFC 7518 |
ECDH-ES+A256KW | P-521 | EC | ECDH-ES key wrapping using key with NIST defined P-521 elliptic curve to create a 512 bits key as defined in RFC 7518 |
ECDH-ES+A256KW | X25519 | OKP | ECDH-ES with X25519 (RFC 7748 section 5) to create a 256 bits key. The underlying curve is actually Curve25519 , however when used in the context of Diffie-Hellman the identifier of X25519 is used |
ECDH-1PU+A256KW | P-256 | EC | ECDH-1PU key wrapping using key with NIST defined P-256 elliptic curve to create a 256 bits key as defined in ECDH-1PU |
ECDH-1PU+A256KW | P-384 | EC | ECDH-1PU key wrapping using key with NIST defined P-384 elliptic curve to create a 256 bits key as defined in ECDH-1PU |
ECDH-1PU+A256KW | P-521 | EC | ECDH-1PU key wrapping using key with NIST defined P-521 elliptic curve to create a 512 bits key as defined in ECDH-1PU |
ECDH-1PU+A256KW | X25519 | OKP | ECDH-1PU X25519 (RFC7748 section 5) to create a 256 bits key as defined in ECDH-1PU |
§ Perfect Forward Secrecy
The mapping of the Perfect Forward Secrecy concepts to DIDComm requires some discussion, due to the lack of a session construct. Please refer to the DIDComm Guidebook for details.
§ Man in the Middle
DIDComm Messaging’s guarantees with respect to man-in-the-middle attacks are easily misunderstood, since DIDs rather than human identity are the concern of the algorithm. Please refer to the DIDComm Guidebook for details.
§ Key IDs
Keys used by DIDComm envelopes will probably be sourced from the DIDs exchanged between two agents. Specifically, both sender and recipient encryption keys are usually retrieved from the respective DID document’s keyAgreement
verification section as per the DID Document Keys definition.
When Alice is preparing an envelope intended for Bob, the packing process should use a key from both hers and Bob’s DID document’s keyAgreement
section.
Assuming Alice has a DID document with the following keyAgreement
definition (source: DID V1 Example 17):
{
"@context": "https://www.w3.org/ns/did/v1",
"id": "did:example:123456789abcdefghi",
...
"keyAgreement": [
// this method can be used to perform key agreement as did:...fghi
"did:example:123456789abcdefghi#keys-1",
// this method is *only* approved for key agreement usage, it will not
// be used for any other verification relationship, so its full description is
// embedded here rather than using only a reference
{
"id": "did:example:123#zC9ByQ8aJs8vrNXyDhPHHNNMSHPcaSgNpjjsBYpMMjsTdS",
"type": "X25519KeyAgreementKey2019", // external (property value)
"controller": "did:example:123",
"publicKeyBase58": "9hFgmPVfmBZwRvFEyniQDBkz9LmV7gDEqytWyGZLmDXE"
}
],
...
}
The envelope packing process would set the skid
header with value did:example:123456789abcdefghi#keys-1
in the envelope’s protected headers and fetch the underlying key to execute ECDH-1PU key derivation for content key wrapping.
Assuming she also has Bob’s DID document which happens to include the following keyAgreement
section:
{
"@context": "https://www.w3.org/ns/did/v1",
"id": "did:example:jklmnopqrstuvwxyz1",
...
"keyAgreement": [
{
"id": "did:example:jklmnopqrstuvwxyz1#key-1",
"type": "X25519KeyAgreementKey2019", // external (property value)
"controller": "did:example:jklmnopqrstuvwxyz1",
"publicKeyBase58": "9hFgmPVfmBZwRvFEyniQDBkz9LmV7gDEqytWyGZLmDXE"
}
],
...
}
Unless previously coordinated in a layer above DIDComm, the default recipients of the envelope SHOULD include all the keyAgreement
entries representing Bob. This allows Bob to decrypt his messages on any device he controls, without sharing keys across his devices. The corresponding kid
header for this recipient MUST have a DID URL pointing to a corresponding verification method in the DID document. This verification method MUST be associated with the keyAgreement
verification relationship and the verification material MUST be retrieved from the DID document to execute the ECDH-1PU key derivation for content key wrapping.
When Bob receives the envelope, the unpacking process on his end resolves the skid
protected header value using Alice’s DID document’s keyAgreement[0]
in order to extract her public key. In Alice’s DID document example above, keyAgreement[0]
is a reference id. It would be resolved from the main verificationMethod[]
of Alice’s DID document (not shown in the example).
Once resolved, the unpacker will then execute ECDH-1PU key derivation using this key and Bob’s own recipient key found in the envelope’s recipients[0]
to unwrap the content encryption key.
§ Protecting the Sender Identity
When employing authenticated encryption, the header of the encrypted message envelope must necessarily reveal the key identifier used by the sender of the message (the skid
). This is used by recipients to resolve the sender’s public key material in order to decrypt the message. In the case of communication between two public DIDs, this may allow outside parties to directly correlate the sender of an encrypted message to a known identity.
If two communicating parties establish single-purpose DIDs (e.g., peer DIDs) for secure communication, then the correlation to any publicly known identities may be limited, although multiple messages referencing the same DIDs will still provide an opportunity for correlation.
A layer of anonymous encryption (employing ECDH-ES) may be applied around an authenticated encryption envelope (employing ECDH-1PU), obscuring the sender’s identity for all but the recipient of the inner envelope. In the case of a message forwarded via mediators, anonymous encryption is automatic.
§ ECDH-1PU key wrapping and common protected headers
When using authcrypt, the 1PU draft mandates the use of the AES_CBC_HMAC_SHA family of content encryption algorithms. To meet this requirement, JWE messages MUST use common epk
, apu
, apv
and alg
headers for all recipient keys. They MUST be set in the protected
JWE section.
As per this requirement, the JWE building must first encrypt the payload, then use the resulting tag
as part of the key derivation process when wrapping the cek
.
To meet this requirement, the above headers are defined as follows:
epk
: generated once for all recipient keys. It MUST be of the same type and curve as all recipient keys since kdf with the sender key must be on the same curve.- Example:
"epk": {"kty": "EC","crv": "P-256","x": "BVDo69QfyXAdl6fbK6-QBYIsxv0CsNMtuDDVpMKgDYs","y": "G6bdoO2xblPHrKsAhef1dumrc0sChwyg7yTtTcfygHA"}
- Example:
apu
: similar toskid
, this is the producer (sender) identifier, it MUST contain theskid
value base64 RawURL (no padding) encoded. Note: this is base64URL(skid
value).- Example for
skid
mentioned in an earlier section above:ZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleXMtMQ
- Example for
apv
: this represents the recipients’kid
list. The list must be alphanumerically sorted,kid
values will then be concatenated with a.
and the final result MUST be base64 URL (no padding) encoding of the SHA256 hash of concatenated list.alg
: this is the key wrapping algorithm, ie:ECDH-1PU+A256KW
.
Even though apu
/apv
are not mandatory JWE recipients headers, they are required in this specification to authenticate the sender via the ECDH-1PU key wrapping algorithm.
A final note about skid
header: since the 1PU draft does not require this header, authcrypt implementations MUST be able to resolve the sender kid from the apu
header if skid
is not set.
§ ECDH-ES key wrapping and common protected headers
When using anoncrypt, any of the valid content encryption algorithms may be used. To meet this requirement, JWE messages MUST use common epk
, apv
and alg
headers for all recipient keys. They MUST be set in the protected
JWE section.
As per this requirement, the JWE building must first encrypt the payload, then use the resulting tag
as part of the key derivation process when wrapping the cek
.
To meet this requirement, the above headers are defined as follows:
epk
: generated once for all recipient keys. It MUST be of the same type and curve as all recipient keys since kdf with the sender key must be on the same curve.- Example:
"epk": {"kty": "EC","crv": "P-256","x": "BVDo69QfyXAdl6fbK6-QBYIsxv0CsNMtuDDVpMKgDYs","y": "G6bdoO2xblPHrKsAhef1dumrc0sChwyg7yTtTcfygHA"}
- Example:
apv
: this represents the recipients’kid
list. The list must be alphanumerically sorted,kid
values will then be concatenated with a.
and the final result MUST be base64 URL (no padding) encoding of the SHA256 hash of concatenated list.alg
: this is the key wrapping algorithm, ie:ECDH-ES+A256KW
.
Note: apu
will not be present when using ECDH-ES.
§ Examples
While the details of encrypting a JWM into a JWE are included in the JWM spec, a few examples are included here for clarity. See section Appendix C.3.
§ Message Signing
A DIDComm message can be signed, either in conjunction with encryption, or independently (e.g., if the message will remain unencrypted).
If a message is signed and encrypted to add non-repudiation, it MUST be signed prior to encryption. This is known as a nested JWM.
§ Algorithms
When a sender would like for a message to feature a non-repudiable digital signature, the JWAs defined below can be used within the structure of a JWS.
Implementations MUST be able to verify all of the following algorithms and MUST support signing with at least one.
Algorithm(JWA) | Description |
---|---|
EdDSA (with crv=Ed25519) | Elliptic curve digital signature with Edwards curves and SHA-512 |
ES256 | Elliptic curve digital signature with NIST p-256 curve and SHA-256 (deprecated) |
ES256K | Elliptic curve digital signature with Secp256k1 keys. |
§ Construction
Construct a JWS with a header like the following (substituting an appropriate kid
value that encodes a key from the authentication
section of the signer’s DID document):
{"typ":"JWM",
"kid":"did:example:123#DhPHHNNMSHPcaSgNpjjsBYpMMjsTdSzC9ByQ8aJs8vrNXy",
"alg":"ES256"}
The kid
MUST be a DID URI that refers to a key specified as an authorization key.
The JWS payload (not shown above) is the Base64url encoded JWM.
When transmitted in a normal JWM fashion, the JSON Serialization MUST be used. Either the General or Flattened form of a JWS is valid. Message recipients MUST be able to process both forms. Message senders using signed messages MAY use either form. Flatted form is sufficient, as only the message sender’s signature is valid, and a single signature may be fully represented in flattened form.
§ Verification
When verifying the signature, an additional check must be performed (ideally, before verifying the JWS). The key used in the signature must be authorized to authenticate the sender by appearing in the authentication
section of the document resolved from the DID in the from
attribute. If this check fails, the signature is inappropriate and MUST be rejected, regardless of its cryptographic correctness.
§ Uses of Signatures
-
Non-Repudiation: DIDComm Encrypted messages are repudiable. If non-repudiation is required for a particular protocol message, the message MUST be signed before encryption.
-
Tamper Resistance: Messages that are shared without encrypting (e.g., Out of Band Invitations may be signed to provide tamper resistance.
-
DID Anchoring: Some types of cryptographic keys support signing but not encrypting. Signed DIDComm messages allow the use of DIDs that are controlled with such keys.
See section Appendix C.2. for examples.
§ DID Rotation
DIDComm Messaging is based on DIDs and their associated DID Documents. Changes to keys and endpoints are the concern of each DID method and are utilized but not managed by DIDComm Messaging. DID Rotation serves a very specific and narrow need to switch from one DID method to another. This is very common at the beginning of a new DIDComm Messaging relationship when a public DID or a temporary DID passed unencrypted is rotated out for a DID chosen for the relationship. As rotation between one DID and another is outside the scope of any DID method, the details of DID Rotation are handled within DIDComm Messaging itself.
A DID is rotated by sending a message of any type to the recipient to be notified of the rotation. The message MUST be encrypted, MUST use the new DID, and MUST include one additional attribute as a message header:
- from_prior: REQUIRED. A JWT, with
sub
: new DID andiss
: prior DID, with a signature from a key authorized by prior DID. Standard JWT Practices for creating and signing the JWT MUST be followed.
Care should be taken when choosing when to rotate from one DID to another. The timing of the rotation may cause some lost messages if messages are arriving rapidly. Coordination must also be made with other agents representing the same DID. Rotating at the very beginning of a relationship or during a quiet period in communication is optimal.
When a message is received from an unknown DID, the recipient SHOULD check for existence of the from_prior
header. The JWT in thefrom_prior
attribute is used to extract the prior DID (iss
) and is checked to verify the validity of the rotation. The recipient then associates the message with context related to the known sender. The new DID and associated DID Document information MUST be used for further communication. The asynchronous and best-effort nature of DIDComm Messaging MAY result in a message sent prior to the rotation being received after the rotation. The message recipient MUST ignore those messages to lower security risk in the case of rotation from a potentially compromised key.
The validity of the DID Rotation is verified by checking the JWT signature against the key indicated in the kid
header parameter. The indicated key MUST be authorized in the DID Document of the prior DID (iss
).
The from_prior
attribute MUST be included in messages sent until the party rotating receives a message sent to the new DID. After the first rotation header is processed, the from
header no longer contains an unknown DID on subsequent messages. As such, no further processing of the from_prior
header is necessary; the header may then be ignored.
§ JWT Details
The JWT used in the from_prior
header is constructed as follows, with appropriate values changed.
Header:
{
"typ": "JWT",
"alg": "EdDSA",
"crv": "ED25519",
"kid": "<key id authorized in prior DID>"
}
Payload:
The Issued At (iat
) JWT property MUST be the datetime of the DID rotation, not of the message being sent.
{
"sub": "<new DID URI>",
"iss": "<prior DID URI>",
"iat": 1516239022
}
§ Example Message Rotating DID
{
"id": "1234567890",
"type": "<message-type-uri>",
"from": "did:example:alice2",
"from_prior": "<JWT with sub:did:example:alice2 and iss:did:example:alice>",
"to": ["did:example:bob"],
"created_time": 1516269022,
"expires_time": 1516385931,
"body": {
"messagespecificattribute": "and its value"
}
}
§ Rotation Limitations
-
This rotation method does not cover cases where a multi-sig is required. Rotations with such requirements should use a more expressive protocol.
-
This rotation method only supports the case where a new DID is used, replacing an old DID which is no longer used within the relationship. Adjustments to DIDs used between different parties that does not fit this narrow use are expected to define a separate protocol to do so. Updates to the already known DID SHOULD use an update to the associated DID Document to convey that information.
§ Ending a Relationship
A relationship may be ended by rotating the DID used in the relationship to nothing. This works the same way as described above, with the following differences:
- Omit ‘sub’ in the
from_prior
JWT. - Send the message without a
from
attribute of the message.
§ When Problems Happen
Detecting, reporting, and handling problems is a challenge in any system. The challenge deepens when systems are decentralized, consist of components written by independent teams, and communicate asynchronously. This is the landscape over which DIDComm operates.
To the extent that it is practical, DIDComm must surface problems, and their supporting contexts, to people and/or automated systems that want to know about them (and perhaps separately, to entities that can actually fix them). (“Practical” here means that it can be done with reasonable effort and without undermining DIDComm’s security, privacy, or other technical goals.)
DIDComm offers several tools to deal with these issues. Individually they are easy to use; collectively they offer attractive robustness and clarity.
§ Low-Level Tools
§ Timeouts
In many cases, a DIDComm message SHOULD use the expires_time
header to announce when its sender will consider the message invalid. This allows for state to be reset in a predictable way. The expires_time
header is so common that it is discussed in the general Message Headers section of the spec. Best practice with timeouts is discussed in the Guidebook.
§ ACKs
DIDComm Messaging provides several tools that let one party acknowledge messages sent by another.
Threading has some implicit built-in ACK semantics. In a two-party protocol that consists of message types mapping unambiguously to progressive steps, each message that moves forward is an implicit ACK of the message that preceded it.
When DIDComm problem reports constitute reactions to a preceding message (as opposed to when they signal problems in external circumstances), they also function as an ACK.
However, more explicit and more powerful ACKs are sometimes needed. They can prove that parties have a shared view of state at a particular time, test the functioning of a transport, help debug surprising silence, fine-tune timeouts, and speed up remedial action.
To facilitate this, any innermost DIDComm plaintext message MAY use the please_ack
header to politely request acknowledgment from a recipient. The header’s value is an array of strings that clarify when the ACK is requested, and what should be ACKed. In this version of the spec, the request is always to ACK as soon as the message is received, and each string in the array is the ID of a message that needs acknowledgment. For terseness, the empty string may be used in the array to mean “the current message.”
For example, suppose Alice is running a rich chat protocol, has previously sent two messages with IDs abc
and def
, and now wants to send a new message with ID xyz
, plus request an acknowledgement of the old ones. Her new message might include this header:
"please_ack": ["abc", "def"]
There is no need to include the empty string (or xyz
) in this array, since any response that honors this request is an implicit ACK of xyz
.
The appropriate response to an ACK request for the current message is the next natural message in the protocol, with an ack
header added. The appropriate response to an ACK request for an old message ID is whatever response it triggered, if any – with an ack
header added. Thus, if Bob had already sent message ghi
after receiving Alice’s abc
and def
, he could resend ghi
with the following ack
header:
"ack": ["abc", "def", "xyz"]
This allows agents to collaborate to recover from a response that was emitted but lost. Future extensions may define additional values (e.g., to implement read receipts, or to request an ACK if no other response is forthcoming after a modest delay).
The presence of the please_ack
header does not create an obligation on the part of the recipient. However, cooperative parties who wish to honor such a request SHOULD include an ack
header on a subsequent message, where the value of the header is an array that contains the id
of one or more messages being acknowledged. Values in this array MUST appear in the order received by whoever is acknowledging, from oldest to most recent.
Note: The
please_ack
header SHOULD NOT be included onforward
messages, and MUST NOT be honored by mediators. It is only for use between ultimate senders and receivers; otherwise, it would add a burden of sourceward communication to mediators, which are defined to send only destward. It would also undermine the privacy of recipients.
Note: Implementations MUST take reasonable steps to avoid an infinite circle of ACKs. Some good rules of thumb are: never honor more than one ACK request for a given message; never send a pure ACK that requests an ACK; never honor a pure ACK request that arrives in response to your own ACK request.
A plaintext message that contains the ack
header is said to be an explicit ACK, no matter what its internal structure or message type is. If an ACK needs to be sent with no other message content, the empty message with an ack
header SHOULD be used.
Particular protocols may wish to design their own message types that convey additional information in an ACK. Custom ACK messages SHOULD include the ack
header if they can appear at more than one step in a protocol, so it’s clear what they are acknowledging. When the message type’s primary purpose is to acknowledge, the type name ack
SHOULD be used, for the sake of consistency.
§ Threads
Any DIDComm message that continues a previously begun application-level protocol MUST use a thid
property that associates it with the prior context. This context is vital for error handling. See Threading.
In addition, messages MAY use the Advanced Sequencing extension to detect gaps in delivery or messages arriving out of order.
§ Problem Reports
DIDComm features a standard mechanism for reporting problems to other entities. These could be parties in the active protocol, or logging products, or internal health monitors, or human tech support staff. Reporting problems remotely is not always possible (e.g., when a sender lacks a route to the other party, or when a recipient’s crypto is incompatible with a sender’s). Using this mechanism is therefore not a general requirement of DIDComm, but it is a best practice because it improves robustness and human experience. (But be aware of some cybersecurity considerations.)
Other entities are notified of problems by sending a simple message called a problem report that looks like this:
{
"type": "https://didcomm.org/report-problem/2.0/problem-report",
"id": "7c9de639-c51c-4d60-ab95-103fa613c805",
"pthid": "1e513ad4-48c9-444e-9e7e-5b8b45c5e325",
"ack": ["1e513ad4-48c9-444e-9e7e-5b8b45c5e325"],
"body": {
"code": "e.p.xfer.cant-use-endpoint",
"comment": "Unable to use the {1} endpoint for {2}.",
"args": [
"https://agents.r.us/inbox",
"did:sov:C805sNYhMrjHiqZDTUASHg"
],
"escalate_to": "mailto:[email protected]"
}
}
-
pthid
- REQUIRED. The value is thethid
of the thread in which the problem occurred. (Thus, the problem report begins a new child thread, of which the triggering context is the parent. The parent context can react immediately to the problem, or can suspend progress while troubleshooting occurs.) -
ack
- OPTIONAL. It SHOULD be included if the problem in question was triggered directly by a preceding message. (Contrast problems arising from a timeout or a user deciding to cancel a transaction, which can arise independent of a preceding message. In such cases,ack
MAY still be used, but there is no strong recommendation.) -
code
- REQUIRED. Deserves a rich explanation; see Problem Codes below. -
comment
- OPTIONAL but recommended. Contains human-friendly text describing the problem. If the field is present, the text MUST be statically associated withcode
, meaning that each time circumstances trigger a problem with the samecode
, the value ofcomment
will be the same. This enables localization and cached lookups, and it has some cybersecurity benefits. The value ofcomment
supports simple interpolation withargs
(see next), where args are referenced as{1}
,{2}
, and so forth. -
args
- OPTIONAL. Contains situation-specific values that are interpolated into the value ofcomment
, providing extra detail for human readers. Each unique problem code has a definition for the args it takes. In this example,e.p.xfer.cant-use-endpoint
apparently expects two values inargs
: the first is a URL and the second is a DID. Missing or null args MUST be replaced with a question mark character (?
) during interpolation; extra args MUST be appended to the main text as comma-separated values. -
escalate_to
- OPTIONAL. Provides a URI where additional help on the issue can be received.
§ Problem Codes
Perhaps the most important feature of each problem report message is its code
field. This required value is the main piece of data that recipient software uses to automate reactions. It categorizes what went wrong.
Problem codes are lower kebab-case. They are structured as a sequence of tokens delimited by the dot character .
, with the tokens being more general to the left, and more specific to the right. Because recipients can do matching by prefix instead of full string, a recipient can recognize and handle broad semantics even if the trailing tokens of the string contain unfamiliar details. In the example below, for example, relatively sophisticated handling is possible even if a recipient only recognizes the e.p.xfer.
portion of the code.
§ Sorter
The leftmost component of a problem code is its sorter. This is a single character that tells whether the consequence of the problem are fully understood. Two values are defined:
e
: This problem clearly defeats the intentions of at least one of the parties. It is therefore an error. A situation with error semantics might be that a protocol requires payment, but a payment attempt was rejected.w
: The consequences of this problem are not obvious to the reporter; evaluating its effects requires judgment from a human or from some other party or system. Thus, the message constitutes a warning from the sender’s perspective. A situation with warning semantics might be that a sender is only able to encrypt a message for some of the recipient’skeyAgreement
keys instead of all of them (perhaps due to an imperfect overlap of supported crypto types). The sender in such a situation might not know whether the recipient considers this an error.
Note: What distinguishes an error from a warning is clarity about its consequences, not its severity. This clarity is inherently contextual. A warning might prove to be just as problematic as an error, once it’s fully evaluated. This implies that the same problem can be an error in some contexts, and a warning in others. In our example above, we imagined a payment failure as an error. But if this problem occurs in a context where retries are expected, and there’s a good chance of future success, perhaps the problem is a warning the first three times it’s reported — then becomes an error when all hope is lost.
§ Scope
Reading left to right, the second token in a problem code is called the scope. This gives the sender’s opinion about how much context should be undone if the problem is deemed an error.
Note: A problem always sorts according to the most pessimistic view that is taken by participants in the protocol. If the sender of a problem report deems it an error, then it is. If the sender deems it a warning, but a recipient with greater context decides that it clearly frustrates their goals, then it becomes an error; see Replying to Warnings. Thus, scope is relevant even if the sender chooses a problem code that starts with
w
.)
The possible values of scope are:
-
p
: The protocol within which the error occurs (and any co-protocols started by and depended on by the protocol) is abandoned or reset. In simple two-party request-response protocols, thep
reset scope is common and appropriate. However, if a protocol is complex and long-lived, thep
reset scope may be undesirable. Consider a situation where a protocol helps a person apply for college, and the problem code ise.p.payment-failed
. With such ap
reset scope, the entire apply-for-college workflow (collecting letters of recommendation, proving qualifications, filling out various forms) is abandoned when the payment fails. Thep
scope is probably too aggressive for such a situation. -
m
: The error was triggered by the previous message on the thread; the scope is one message. The outcome is that the problematic message is rejected (has no effect). If the protocol is a chess game, and the problem code ise.m.invalid-move
, then someone’s invalid move is rejected, and it is still their turn. -
A formal state name from the sender’s state machine in the active protocol. This means the error represented a partial failure of the protocol, but the protocol as a whole is not abandoned. Instead, the sender uses the scope to indicate what state it reverts to. If the protocol is one that helps a person apply for college, and the problem code is
e.get-pay-details.payment-failed
, then the sender is saying that, because of the error, it is moving back to theget-pay-details
state in the larger workflow.
§ Descriptors
After the sorter and the scope, problem codes consist of one or more descriptors. These are kebab-case tokens separated by the .
character, where the semantics get progressively more detailed reading left to right. Senders of problem reports SHOULD include at least one descriptor in their problem code, and SHOULD use the most specific descriptor they can. Recipients MAY specialize their reactions to problems in a very granular way, or MAY examine only a prefix of a problem code.
The following descriptor tokens are defined. They can be used by themselves, or as prefixes to more specific descriptors. Additional descriptors — particularly more granular ones — may be defined in individual protocols.
Token | Value of comment string |
Notes |
---|---|---|
trust |
Failed to achieve required trust. | Typically this code indicates incorrect or suboptimal behavior by the sender of a previous message in a protocol. For example, a protocol required a known sender but a message arrived anoncrypted instead — or the encryption is well formed and usable, but is considered weak. Problems with this descriptor are similar to those reported by HTTP’s 401 , 403 , or 407 status codes. |
trust.crypto |
Cryptographic operation failed. | A cryptographic operation cannot be performed, or it gives results that indicate tampering or incorrectness. For example, a key is invalid — or the key types used by another party are not supported — or a signature doesn’t verify — or a message won’t decrypt with the specified key. |
xfer |
Unable to transport data. | The problem is with the mechanics of moving messages or associated data over a transport. For example, the sender failed to download an external attachment — or attempted to contact an endpoint, but found nobody listening on the specified port. |
did |
DID is unusable. | A DID is unusable because its method is unsupported — or because its DID doc cannot be parsed — or because its DID doc lacks required data. |
msg |
Bad message. | Something is wrong with content as seen by application-level protocols (i.e., in a plaintext message). For example, the message might lack a required field, use an unsupported version, or hold data with logical contradictions. Problems in this category resemble HTTP’s 400 status code. |
me |
Internal error. | The problem is with conditions inside the problem sender’s system. For example, the sender is too busy to do the work entailed by the next step in the active protocol. Problems in this category resemble HTTP’s 5xx status codes. |
me.res |
A required resource is inadequate or unavailable. | The following subdescriptors are also defined: me.res.net , me.res.memory , me.res.storage , me.res.compute , me.res.money |
req |
Circumstances don’t satisfy requirements. | A behavior occurred out of order or without satisfying certain preconditions — or circumstances changed in a way that violates constraints. For example, a protocol that books plane tickets fails because, halfway through, it is discovered that all tickets on the flight have been sold. |
req.time |
Failed to satisfy timing constraints. | A message has expired — or a protocol has timed out — or it is the wrong time of day/day of week. |
legal |
Failed for legal reasons. | An injunction or a regulatory requirement prevents progress on the workflow. Compare HTTP status code 451 . |
§ Replying to Warnings
When Alice sends a w.*
problem report to Bob, and Bob decides that the warning is actually an error, he SHOULD reply to Alice to let her know about the consequences of his evaluation. Bob’s reply is another problem report. It looks very similar to Alice’s original message, except:
- The
code
in Bob’s message now begins withe.
. The remainder of the code MAY (often will be) identical, but this is not required; if Bob knows more details than Alice did, he SHOULD provide them. The scope in Bob’s code MUST be at least as broad as the scope in Alice’s original message. (For example, Bob MUST NOT use scopem
to say the protocol continues with only a bad message ignored, if Alice’s original warning said she considered the scope to bep
.) - The
args
property may or may not match. - The
id
header for Bob’s message has a new value. (Bob’s message and Alice’s MUST both be part of the same thread, so Bob’s message is processed as a reply to Alice’s. See Threading.)
§ Cascading Problems
Many problems may be experienced during a long-running or complex protocol. Implementers must have the option of tolerating and recovering from them, if we want robustness; perhaps several network retries will be followed by eventual success. However, care must be exercised to prevent situations where malformed or careless problem reports trigger infinite recursion or vicious cycles:
- Implementations SHOULD consider implementing a circuit breaker design pattern to prevent this problem.
- Timeouts SHOULD be used judiciously.
- Implementations SHOULD use their own configuration or judgment to establish some type of max error count as they begin a protocol instance. This limit could be protocol-specific, and could be evaluated per unit time (e.g., in a human chat protocol of infinite duration, perhaps the limit is max errors per hour rather than max errors across all time). If implementations establish such a limit, they SHOULD check to see whether this count has been exceeded, both when they receive and when they emit errors. If the limit is crossed as a result of a problem report they receive, they SHOULD send back a problem report with
"code": "e.p.req.max-errors-exceeded"
to abort the protocol. If the limit is crossed as a result of an error they are emitting, they MUST NOT emit the problem report for the triggering error; instead, they MUST emit a problem report with"code": "e.p.req.max-errors-exceeded"
to abort the protocol. In either case, they MUST cease responding to messages that use thethid
of that protocol instance, once this limit has been crossed.
§ Route Tracing
To troubleshoot routing issues, DIDComm offers a header, trace
. Any party that processes a DIDComm plaintext message containing this header MAY do an HTTP POST of a route trace report to the URI in the header’s value. A trace report is a message that looks like this:
{
"type": "https://didcomm.org/trace/2.0/trace_report",
"pthid": "98fd8d72-80f6-4419-abc2-c65ea39d0f38.1",
"handler": "did:example:1234abcd#3",
"traced_type": "https://didcomm.org/routing/2.0/forward",
}
The value of pthid
is always the message ID that triggered the trace. The value of handler
is an arbitrary string that identifies the agent, service, or piece of software responding to the trace.
For the sake of consistency, this message uses some structural conventions that match a DIDComm plaintext message. However, it need not be understood as a message in a DIDComm protocol. It can be parsed by any consumer of generic JSON, it can be transmitted using any channel that suits the sender and receiver, and it is not associated with any interaction state.
Note: This mechanism is not intended to profile timing or performance, and thus does not cover the same problem space as technologies like OpenTelemetry. It also spans trust domains (paralleling a message’s journey from Alice to a web service hosting Bob’s endpoint, to Bob himself) — and thus differs in scope from in-house logging and monitoring technolgies like Splunk and Logstash/Kibana. Although DIDComm tracing could be integrated with these other technologies, doing so in a methodical way is probably an antipattern; it may indicate a misunderstanding about its purpose as a tool for ad hoc debugging or troubleshooting between unrelated parties.
For example, in a message for Bob that is double-wrapped (once for his external mediator and once for his cloud agent), three plaintext messages might contain trace
headers:
- The outermost message, decrypted by Bob’s external mediator, containing forwarding instructions to Bob’s cloud agent.
- The center message, decrypted by Bob’s cloud agent, containing an inner encrypted payload and instructions to forward it to Bob’s DID.
- The inner message, seen by Bob’s iPhone.
If Alice, the sender of this message, includes a trace
header on each one, and if handlers of this message along the route cooperate with her request to trace, then Alice can learn where in a route a message delivery is failing.
Tracing has security, privacy, and performance implications. Support for tracing is not required of DIDComm implementations, but it is recommended for parties that need sophisticated debugging. Parties that implement tracing MUST decide whether or not to honor trace requests based upon a policy that ensures accountability and transparency, and MUST default to reject tracing requests unless they have independent reason to believe that appropriate safeguards are in place.
§ Threading
DIDComm provides threading as foundation for extremely powerful protocol features. For background on the intent and best practices for threading, please see the DIDComm Guidebook.
§ Message IDs
All plaintext DIDComm messages MUST have an id
property, declared in the JWM headers. A message without an id
property SHOULD be considered invalid and SHOULD be rejected; it MUST NOT be interpreted as part of a multi-message interaction.
The value of id
is a short (<=32 bytes) string consisting entirely of unreserved URI characters — meaning that it is not necessary to percent encode the value to incorporate it in a URI. Beyond this requirement, its format is not strongly constrained, but use of UUIDs (RFC 4122) is recommended. Because of the affinity for UUIDs, this field inherits UUID case-sensitivity semantics: it SHOULD be written in lower case but MUST be compared case-insensitively.
The value of an id
property SHOULD be globally, universally unique; at the very least, it MUST be unique across all interactions visible to the set of parties that see a given set of interactions.
§ Threads
A thread is uniquely identified by a thread ID. The thread ID is communicated by including a thid
header in the JWM plaintext. The value of thid
MUST conform to the same constraints as the value of id
. The DIDComm plaintext message that begins a thread MAY declare this property for the new thread. If no thid
property is declared in the first message of an interaction, the id
property of the message MUST be treated as the value of the thid
as well; that is, the message is interpreted as if both id
and thid
were present, containing the same value.
All subsequent messages in a thread MUST include a thid
header that contains the same value as the thid
set in the first message of the thread. Messages that do not share the same thid
MUST NOT be considered a part of the same thread.
§ Parent Threads
When one interaction triggers another, the first interaction is called the parent of the second. This MAY be modeled by incorporating a pthid
header in the JWM plaintext of the child. The value of the child’s pthid
header MUST obey the same constraints as thid
and id
values.
Suppose a DIDComm-based protocol (and therefore, a thread of messages) is underway in which an issuer wants to give a credential to a holder. Perhaps the issuer asks the prospective holder of the credential to pay for what they’re about to receive. For many reasons (including composability, encapsulation, reusability, and versioning), negotiating and consummating payment is best modeled as a separable interaction from credential exchange. Thus, a new thread of messages (dedicated to payment) begins. In this example, the credential issuance interaction (message thread 1) is the parent of the payment interaction (message thread 2). The first message in thread 2 MUST contain a pthid
header that references the thid
from thread 1:
{
"id": "new-uuid-for-payment-thread",
"pthid": "id-of-old-credential-issuance-thread",
When a child protocol is a simple two-party interaction, mentioning the pthid
in the first message of the child interaction is enough to establish context. However, in protocols involving more than two parties, the first message of the child protocol may not be seen by everyone, so simply mentioning pthid
once may not provide enough context. Therefore, the rule is that each party in a child protocol MUST learn the identity of the parent thread via the first child protocol message they see. The simplest way to ensure this is to mention the pthid
with every message in the child protocol.
§ Message URIs
The id
, thid
, and pthid
properties of any DIDComm message may be combined to form a URI that uniquely identifies the message (e.g., in debuggers, in log files, in archives). Such a scheme is out of scope for this spec, and support for it is OPTIONAL for implementers.
§ Gaps, Resends, and Sophisticated Ordering
Message IDs and threads can be used to build very powerful features for detecting missing and out-of-order messages — and to recover from them. For more information, see the Advanced Sequencing Extension.
§ Transports
§ Summary
DIDComm Messaging is designed to be transport-independent. Regardless of transport, the encryption envelope provides confidentiality, integrity, and (for authcrypt) authentication, providing trust as a feature of each message. However, each transport does have unique features; DIDComm defines conventions that help to align usage. The normative statements below do not prevent someone from using DIDComm + a transport in custom ways; they simply specify one collection of choices that is standardized.
§ Delivery
DIDComm Transports serve only as message delivery. No information about the effects or results from a message is transmitted over the same connection.
§ Transport Requirements
Each transport MUST define:
- format of
serviceEndpoint
uri
: Which URI schemes are used (if URI), or the properties of the object (if object). - how to actually send messages: e.g., through HTTPS POST, through dial protocol (libp2p), etc.
- how IANA media types of the content are provided, e.g., through
Content-Type
header, etc. - where additional context definition is hosted, e.g., in case the
serviceEndpoint
object has extra properties specific to the transport.
§ Agent Constraint Disclosure
As mentioned above, DIDComm Messaging is designed to be transport-independent. Given the wide variety of transports that can be conceived, some agents may have additional constraints that they would like to disclose regarding how they communicate. These generic and customizable constraints may vary over time or be unique to special categories of agents and may be expressed using using the Discover Features Protocol. The following includes a subset of the many different agent constraints that can be expressed:
§ max_receive_bytes
The optional max_receive_bytes
constraint is used to specify the total length of the DIDComm header plus the size of the message payload that an agent is willing to receive. While the core DIDComm protocol itself does not impose a specific maximum size for DIDComm messages, a particular agent may have specific requirements that necessitate only receiving messages up to a specific length. For example, IoT devices that may not have the bandwidth or buffering capabilites to accept large messages may choose to only accept small messages. The definition of large vs small is subjective and may be defined according to the needs of each implementing agent.
When a max_receive_bytes
constraint is specified, any received message that exceeds the agent’s stated maximum may be discarded. It is recommended that the agent imposing the constraint send a problem report citing the constraint as the cause of a reception error. The associated Problem Code is me.res.storage.message_too_big
. However, sending a response in the opposite direction on a DIDComm channel may not always be possible, given simplex transports and complex delivery routes. Therefore, it is also appropriate to emit an error at the transport level, such as HTTP 413 Request Too Large
. Agents that receive problem reports or transport-level errors, or that experience a lack of response, may test whether this constraint is the cause using standard DIDComm troubleshooting techniques, such as Route Tracing.
Prior to transmission, a sending agent may query a receiving agent for a maximum message length limitation using the Discover Features Protocol. Using the Discover Features Protocol, a max_receive_bytes
query message may look like this:
{
"type": "https://didcomm.org/discover-features/2.0/queries",
"id": "yWd8wfYzhmuXX3hmLNaV5bVbAjbWaU",
"body": {
"queries": [
{ "feature-type": "constraint", "match": "max_receive_bytes" }
]
}
}
In response to a max_receive_bytes
request, a Discover Features disclose message may look like this:
{
"type": "https://didcomm.org/discover-features/2.0/disclose",
"thid": "yWd8wfYzhmuXX3hmLNaV5bVbAjbWaU",
"body":{
"disclosures": [
{
"feature-type": "constraint",
"id": "max_receive_bytes",
"max_receive_bytes": "65536"
}
]
}
}
§ Reference
§ HTTPS
HTTPS transports are an effective way to send a message to another online agent.
- Messages MUST be transported via HTTPS POST.
- The IANA media type for the POST request MUST be set to the corresponding media type, e.g.,
application/didcomm-encrypted+json
. - A successful message receipt MUST return a code in the 2xx HTTPS Status Code range. 202 Accepted is recommended.
- POST requests are used only for one-way transmission from sender to receiver; responses don’t flow back in the web server’s HTTP response.
- HTTPS Redirects SHOULD be followed. Only temporary redirects (307) are acceptable. Permanent endpoint relocation should be managed with a DID Document update.
- Using HTTPS with TLS 1.2 or greater with a cipher suite providing Perfect Forward Secrecy (PFS) allows a transmission to benefit from PFS that’s already available at the transport level.
§ WebSockets
Websockets are an efficient way to transmit multiple messages without the overhead of individual requests. This is useful in a high bandwidth situation.
- Each message MUST be transmitted individually; if encryption or signing are used, the unit of encryption or signing is one message only.
- The trust of each message MUST be associated with DIDComm encryption or signing, not from the socket connection itself.
- Websockets are used only for one-way transmission from sender to receiver; responses don’t flow back the other way on the socket.
- Using Secure Websockets (wss://) with TLS 1.2 or greater with a cipher suite providing Perfect Forward Secrecy (PFS) allows a transmission to benefit from PFS that’s already available at the transport level.
- When using STOMP over WebSocket, the
content-type
header isapplication/didcomm-encrypted+json
as in the HTTPS message.
§ Protocols
The primitives described earlier in this spec standardize basic behaviors in peer-to-peer secure communication: signing, encryption, plaintext structure, passing metadata in headers, reporting problems, attaching content, etc. Developers can use these primitives to design any number of high-trust, application-level protocols for dedicated purposes, and then compose them into ever broader workflows in arbitrary, powerful ways. Community efforts to design and share protocols can happen anywhere; one locus of that work is didcomm.org.
A few higher-level protocols are especially fundamental, in that they bootstrap communication and discovery. Those protocols and their underlying principles are described below.
§ Protocol Identifier URI
Each protocol constructed atop DIDComm Messaging is uniquely identified and versioned by a Protocol Identifier URI (PIURI).
The PIURI MUST be composed of a sequence of tokens as follows:
doc-uri delim protocol-name/semver
As ABNF:
protocol-identifier-uri = doc-uri delim protocol-name "/" semver
delim = "?" / "/" / "&" / ":" / ";" / "="
It can be loosely matched and parsed with the following regex:
(.*?)([a-z0-9._-]+)/(\d[^/]*)/?$
The PIURI for an imaginary protocol to schedule lunch appointments might resemble one of the following:
https://didcomm.org/lets_do_lunch/1.0
https://example.com/protocols?which=lets_do_lunch/1.0
did:example:1234567890;spec/lets_do_lunch/1.0/ping
https://github.com/myorg/myproject/tree/master/docs/lets_do_lunch/1.0
The PIURI for a given protocol SHOULD resolve to human-friendly documentation about the protocol.
§ Message Type URI
A Message Type URI (MTURI) identifies plaintext message types unambiguously. Since the names of message types are only unique within the context of the protocol they embody, an MTURI begins with a prefix that is a PIURI, and then adds a message name token as a suffix.
Standardizing MTURI format is important because MTURIs are parsed by agents and used to map messages to handlers. Code will look at this string and say, “Do I have something that can handle this message type inside protocol X version Y?” When that analysis happens, it must do more than compare the string for exact equality. It may need to check for semver compatibility, and it has to compare the protocol name and message type name ignoring case and punctuation.
The MTURI MUST be composed of a sequence of tokens as follows:
protocol-identifier-uri/message-type-name
As ABNF:
message-type-uri = protocol-identifier-uri "/" message-type-name
protocol-identifier-uri = doc-uri delim protocol-name "/" semver
delim = "?" / "/" / "&" / ":" / ";" / "="
protocol-name = identifier
protocol-version = semver
message-type-name = identifier
identifier = alpha *(*(alphanum / "_" / "-" / ".") alphanum)
It can be loosely matched and parsed with the following regex:
(.*?)([a-z0-9._-]+)/(\d[^/]*)/([a-z0-9._-]+)$
A match will have capturing groups of (1) = doc-uri
, (2) = protocol-name
,
(3) = protocol-version
, and (4) = message-type-name
.
Building on our previous examples of lets_do_lunch
PIURIs, the MTURI of a proposal
message in that protocol might be something like:
https://didcomm.org/lets_do_lunch/1.0/proposal
https://example.com/protocols?which=lets_do_lunch/1.0/proposal
did:example:1234567890;spec/lets_do_lunch/1.0/proposal
https://github.com/myorg/myproject/tree/master/docs/lets_do_lunch/1.0/proposal
§ Semver Rules
The version numbers embedded in PIURIs and MTURIs MUST follow familiar semver rules, such that two parties that support the same protocol at the same major version but different minor versions could theoretically interoperate with a feature profile that matches the older of their two versions. (Of course, this does not guarantee interoperability; the party supporting a newer version still chooses whether they want to support the older version or not. Semver rules simply define how a version mismatch must be interpreted.)
The major component of a protocol’s semver value MUST be updated under either of the following conditions:
- A change breaks important assumptions about the intent, preconditions, postconditions, or state machine of a protocol.
- A change adds or removes required fields or required messages.
The minor component of a protocol’s semver value MUST be updated when a change does not justify a major number update, but it is more than a trivial update to documentation. Examples of minor version changes include adding new, optional fields or deprecating existing fields.
The patch component of a protocol’s semver value is not used in MTURIs and PIURIs.
§ Routing Protocol 2.0
The routing protocol defines how a sender and a recipient cooperate, using a partly trusted mediator, to facilitate message delivery. No party is required to know the full route of a message.
§ Name and Version
The name of this protocol is “Routing Protocol”, and its version is “2.0”. It is uniquely identified by the PIURI:
https://didcomm.org/routing/2.0
§ Roles
There are 3 roles in the protocol: sender, mediator, and recipient. The sender emits messages of type forward
to the mediator. The mediator unpacks (decrypts) the payload of an encrypted forward
message and passes on the result (a blob that probably contains a differently encrypted payload) to the recipient.
Note: the protocol is one-way; the return route for communication might not exist at all, or if it did, it could invert the roles of sender and receiver and use the same mediator, or it could use one or more different mediators, or it could use no mediator at all. This is a separate concern partly specified by the service endpoints in the DID docs of the sender and receiver, and partly explored in other protocols.
Note: When the mediator is the routing agent of a single identity subject like Alice, the logical receiver is Alice herself, but the physical receiver may manifest as multiple edge devices (a phone, a laptop, a tablet). From the perspective of this protocol, multiplexing the send from mediator to receiver is out of scope for interoperability — compatible and fully supported, but not required or specified in any way.
In this protocol, the sender and the receiver never interact directly; they only interact via the mediator.
The sender can add the standard expires_time
to a forward
message. An additional header, delay_milli
is also possible; this allows the sender to request that a mediator wait a specified number of milliseconds before delivering. Negative values mean that the mediator should randomize delay by picking a number of milliseconds between 0 and the absolute value of the number, with a uniform distribution.
The mediator is NOT required to support or implement any of these semantics; only the core forwarding behavior is indispensable. If a mediator sees a header that requests behavior it doesn’t support, it MAY return a problem-report to the sender identifying the unsupported feature, but it is not required to do so.
Note: The
please_ack
header SHOULD NOT be included onforward
messages, and MUST NOT be honored by mediators. It is only for use between ultimate senders and receivers; otherwise, it would add a burden of sourceward communication to mediators, and undermine the privacy of recipients.
§ States
Since data flow is normally one-way, and since the scope of the protocol is a single message delivery, a simplistic way to understand it might be as two instances of a stateless notification pattern, unfolding in sequence.
However, this doesn’t quite work on close inspection, because the mediator is at least potentially stateful with respect to any particular message; it needs to be if it wants to implement delayed delivery or retry logic. (Or, as noted earlier, the possibility of sending to multiple physical receivers. Mediators are not required to implement any of these features, but the state machine needs to account for their possibility.) Plus, the notification terminology obscures the sender and receiver roles. So we use the following formalization:
]
§ Messages
The only message in this protocol is the forward
message. A simple and common version of a forward
message might look like this:
{
"type": "https://didcomm.org/routing/2.0/forward",
"id": "abc123xyz456",
"to": ["did:example:mediator"],
"expires_time": 1516385931,
"body":{
"next": "did:foo:1234abcd"
},
"attachments": [
// The payload(s) to be forwarded
]
}
next
- REQUIRED. The identifier of the party to send the attached message to.attachments
- REQUIRED. The DIDComm message(s) to send to the party indicated in thenext
body attribute. This content should be encrypted for the next recipient.
When the internal message expires, it’s a good idea to also include an expiration for forward requests. Include the expires_time
header with the appropriate value.
The value of the next
field is typically a DID. However, it may also be a key, for the last hop of a route. The routingKeys
array in the serviceEndpoint
portion of a DID doc allow a party to list keys that should receive inbound communication, with encryption multiplexed so any of the keys can decrypt. This supports a use case where Alice wants to process messages on any of several devices that she owns.
The attachment(s) in the attachments
field are able to use the full power of DIDComm attachments, including features like instructing the receiver to download the payload content from a CDN.
§ Rewrapping
Normally, the payload attached to the forward
message received by the mediator is transmitted directly to the receiver with no further packaging. However, optionally, the mediator can attach the opaque payload to a new forward
message (appropriately anoncrypted), which then acts as a fresh outer envelope for the second half of the delivery. This rewrapping means that the “onion” of packed messages stays the same size rather than getting smaller as a result of the forward operation:
Rewrapping mode is invisible to senders, but mediators need to know about it, since they change their behavior as a result. Receivers also need to know about it, because it causes them to receive a double-packaged message instead of a singly-packaged one. The outer envelope is a forward
message where to
is the receiver itself.
Why is such indirection useful?
- It lets the mediator decorate messages with its own timing and tracing mixins, which may aid troubleshooting. (This would otherwise be impossible, since the inner payload is an opaque blob that is almost certainly tamper-evident and encrypted.)
- It lets the mediator remain uncommitted to whether the next receiver is another mediator or not. This may provide flexibility in some routing scenarios.
- It lets the mediator change the size of the message by adding or subtracting noise from the content.
- It allows for dynamic routing late in the delivery chain.
These last two characteristics could provide the foundation of mixnet features for DIDComm; however, such functionality is out of scope in this spec.
§ Sender Process to Enable Forwarding
- Construct a plaintext message, M.
- If appropriate, sign M.
- Encrypt M for each party that is an intended recipient. Assuming each recipient has several keys, corresponding to several devices, but that all the keys are of the same type, this produces a single message, N, for each recipient — and N is decryptable on any device the recipient is using. If Alice is sending to Bob and Carol, this step produces NBob and NCarol, which have identical plaintext but different encrypted embodiments.
- Perform a wrapping process that loops in reverse order over all items in the
routingKeys
array of the service endpoint for the DID document that corresponds to the intended recipient of N. For each item X in that array, beginning at the end of the array and working to its beginning, Sender creates a new plaintextforward
message, attaches the current N, and encrypts it for X. The output is a new encrypted message, N’, that is treated as N in the next round of wrapping. - Transmit the fully wrapped version of N to the
uri
given in the associatedserviceEndpoint
of the recipient’s DID document.
The party that receives it will have the ability to decrypt. The output of decryption will be a forward
message that indicates the next hop from routingKeys
in the next
attribute of its body
. It will also have an encrypted attachment. This attachment is forwarded to the next hop. This unwrapping and forwarding is repeated until the message reaches its final destination.
§ Mediator Process
Prior to using a Mediator, it is the recipient’s responsibility to coordinate with the mediator. Part of this coordination informs them of the next
address(es) expected, the endpoint, and any routing keys to be used when forwarding messages. That coordination is out of the scope of this spec.
- Receive ‘forward’ message.
- Retrieve service endpoint pre-configured by recipient (
next
attribute). - Transmit
payload
message to service endpoint in the manner specified in the [transports] section.
The recipient (next
attribute of ‘forward’ message) may have pre-configured additional routing keys with the mediator that were not present in the DID Document and therefore unknown to the original sender. If this is the case, the mediator should wrap the attached payload
message into an additional Forward message once per routing key. This step is performed between (2) and (3).
§ DID Document Keys
Ideally, all keys declared in the keyAgreement
section of a given recipient’s DID document are used as target keys when encrypting a message. To encourage this, DIDComm encrypts the main message content only once, using an ephemeral content encryption key, and then encrypts the relatively tiny ephemeral key once per recipient key. This “multiplexed ecnryption” is efficient, and it allows a recipient to change devices over the course of a conversation without prior arrangement.
However, practical considerations can frustrate this ideal. If a recipient’s DID document declares keys of different types, a sender has to prepare more than one encryption envelope — and if not all of a recipient’s key types are supported by the sender, the goal is simply unachievable.
In addition, if a sender is routing the same message to more than one recipient (not just more than one key of the same recipient), the sender has to wrap the message differently because it will flow through different mediators.
This leads to a rule of thumb rather than a strong normative requirement: a sender SHOULD encrypt for as many of a recipient’s keys as is practical.
The details of key representation are described in the Verification Methods section of the DID Core Spec.
Keys used in a signed JWM are declared in the DID Document’s authentication
section.
§ Service Endpoint
Parties who wish to communicate via DIDComm Messaging MAY tell other parties how to reach them by declaring a serviceEndpoint
block in their DID document. (It is also possible to convey this information in other ways, but they are out of scope for this spec.)
The relevant entry in the DID document matches this format:
{
"id": "did:example:123456789abcdefghi#didcomm-1",
"type": "DIDCommMessaging",
"serviceEndpoint": [{
"uri": "https://example.com/path",
"accept": [
"didcomm/v2",
"didcomm/aip2;env=rfc587"
],
"routingKeys": ["did:example:somemediator#somekey"]
}]
}
id
- REQUIRED. Must be unique, as required in DID Core. No special meaning should be inferred from the id
chosen.
type
- REQUIRED. MUST be DIDCommMessaging
.
serviceEndpoint
- REQUIRED. MUST contain an ordered list of objects. Each represents a DIDComm Service Endpoint URI and its associated details. The order of the endpoints SHOULD indicate the DID Document owner’s preference in receiving messages. Any endpoint MAY be selected by the sender, typically by protocol availability or preference. A message should be delivered to only one of the endpoints specified.
Each object has the following properties:
uri
- REQUIRED. MUST contain a URI for a transport specified in the [transports] section of this spec, or a URI from Alternative Endpoints. It MAY be desirable to constraint endpoints from the [transports] section so that they are used only for the reception of DIDComm messages. This can be particularly helpful in cases where auto-detecting message types is inefficient or undesirable.
accept
- OPTIONAL. An array of media types in the order of preference for sending a message to the endpoint.
These identify a profile of DIDComm Messaging that the endpoint supports.
If accept
is not specified, the sender uses its preferred choice for sending a message to the endpoint.
Please see Negotiating Compatibility for details.
routingKeys
- OPTIONAL. An ordered array of strings referencing keys to be used when preparing the message for transmission as specified in Sender Process to Enable Forwarding, above.
§ Failover
If the transmission of a message fails, the sender SHOULD try another endpoint or try delivery at a later time.
§ Using a DID as an endpoint
In addition to the sorts of URIs familiar to all web developers, it is possible to use a DID as the uri
value in a serviceEndpoint
. This is useful when a recipient sits behind a mediator, because it allows the mediator to rotate its keys or update its own service endpoints without disrupting communication between sender and recipient. In such cases, the DID (which belongs to the mediator) is resolved. Inside the resulting DID document, a serviceEndpoint
with type DIDCommMessaging
MUST exist. The keyAgreement
keys of the mediator are implicitly prepended to the routingKeys
section from the message recipient’s DID Document as per the process in Sender Process to Enable Forwarding.
A DID representing a mediator SHOULD NOT use alternative endpoints in its own DID Document to avoid recursive endpoint resolution. Using only the URIs described in Transports will prevent such recursion.
Endpoint Example 1: Mediator
{
"id": "did:example:123456789abcdefghi#didcomm-1",
"type": "DIDCommMessaging",
"serviceEndpoint": [{
"uri": "did:example:somemediator"
}]
}
The message is encrypted to the recipient, then wrapped in a ‘forward’ message encrypted to the keyAgreement keys within the did:example:somemediator
DID Document, and transmitted to the URIs present in the did:example:somemediator
DID Document with type DIDCommMessaging
.
Endpoint Example 2: Mediator + Routing Keys
{
"id": "did:example:123456789abcdefghi#didcomm-1",
"type": "DIDCommMessaging",
"serviceEndpoint": [{
"uri": "did:example:somemediator",
"routingKeys": ["did:example:anothermediator#somekey"]
}]
}
The message is encrypted to the recipient, then wrapped in a forward
message encrypted to did:example:anothermediator#somekey
. That message is wrapped in a ‘forward’ message encrypted to ‘keyAgreement’ keys within the did:example:somemediator
DID Document, and transmitted to the URIs present in the did:example:somemediator
DID Document with type DIDCommMessaging
.
§ Out Of Band Messages
§ URL & QR Codes
When passing a DIDComm message between two parties, it is often useful to present a message in the form of a URL or encoded into the form of a QR code for scanning with a smartphone or other camera. The format for a QR code is simply the encoded URL form of a message.
§ Privacy Considerations
Any information passed via a URL or QR code is unencrypted, and may be observed by another party. This lack of privacy must be minded in two different ways.
First, no private information may be passed in the message. Private information should be passed between parties in encrypted messages only. Any protocol message that contains private information should not be passed via URL or QR code.
Second, any identifiers passed in a message sent via URL or QR code must no longer be considered private. Any DID used or other identifier no longer considered private MUST be rotated over a secure connection if privacy is required.
§ Message Correlation
The id
of the message passed in a URL or a QR code is used as the pthid
on a response sent by the recipient of this message. The response recipient can use the pthid
to correlate it with the original message.
§ Invitation
Each message passed this way must be contained within an out-of-band
message, as described below.
The out-of-band protocol consists in a single message that is sent by the sender.
{
"type": "https://didcomm.org/out-of-band/2.0/invitation",
"id": "<id used for context as pthid>",
"from":"<sender's did>",
"body": {
"goal_code": "issue-vc",
"goal": "To issue a Faber College Graduate credential",
"accept": [
"didcomm/v2",
"didcomm/aip2;env=rfc587"
],
},
"attachments": [
{
"id": "request-0",
"mime_type": "application/json",
"data": {
"json": "<json of protocol message>"
}
}
]
}
The items in the message are:
type
- REQUIRED. The header conveying the DIDComm MTURI.id
- REQUIRED. This value MUST be used as the parent thread ID (pthid
) for the response message that follows. This may feel counter-intuitive — why not it in thethid
of the response instead? The answer is that putting it inpthid
enables multiple, independent interactions (threads) to be triggered from a single out-of-band invitation.from
- REQUIRED for OOB usage. The DID representing the sender to be used by recipients for future interactions.goal_code
- OPTIONAL. A self-attested code the receiver may want to display to the user or use in automatically deciding what to do with the out-of-band message.goal
- OPTIONAL. A self-attested string that the receiver may want to display to the user about the context-specific goal of the out-of-band message.accept
- OPTIONAL. An array of media types in the order of preference for sending a message to the endpoint. These identify a profile of DIDComm Messaging that the endpoint supports. Ifaccept
is not specified, the sender uses its preferred choice for sending a message to the endpoint. Please see Negotiating Compatibility for details.attachments
- OPTIONAL. An array of attachments that will contain the invitation messages in order of preference that the receiver can use in responding to the message. Each message in the array is a rough equivalent of the others, and all are in pursuit of the statedgoal
andgoal_code
. Only one of the messages should be chosen and acted upon. (While the JSON form of the attachment is used in the example above, the sender could choose to use the base64 form.)
When encoding a message in a URL or QR code, the sender does not know which protocols are supported by the recipient of the message. Encoding multiple alternative messages is a form of optimistic protocol negotiation that allows multiple supported protocols without coordination
§ Standard Message Encoding
Using a standard message encoding allows for easier interoperability between multiple projects and software platforms. Using a URL for that standard encoding provides a built in fallback flow for users who are unable to automatically process the message. Those new users will load the URL in a browser as a default behavior, and may be presented with instructions on how to install software capable of processing the message. Already onboarded users will be able to process the message without loading in a browser via mobile app URL capture, or via capability detection after being loaded in a browser.
The standard message format is a URL with a Base64URLEncoded plaintext JWM json object as a query parameter.
The URL format is as follows, with some elements described below:
https://<domain>/<path>?_oob=<encodedplaintextjwm>
<domain>
and <path>
should be kept as short as possible, and the full URL should return human readable instructions when loaded in a browser. This is intended to aid new users. The _oob
query parameter is required and is reserved to contain the DIDComm message string. Additional path elements or query parameters are allowed, and can be leveraged to provide coupons or other promise of payment for new users.
_oob
is a shortened form of Out of Band, and was chosen to not conflict with query parameter names in use at a particular domain. When the query parameter is detected, it may be assumed to be an Out Of Band message with a reasonably high confidence.
When this spec was written, the
didcomm://
URL scheme was in active use for deep linking in mobile apps, and had features that intersect with the OOB protocol described here. That scheme is defined elsewhere; we only note it here to advise against its overloading for other purposes.
The <encodedplaintextjwm>
is a JWM plaintext message that has been base64-url encoded.
encodedplaintextjwm = b64urlencode(<plaintextjwm>)
During encoding, whitespace from the json string should be eliminated to keep the resulting out-of-band message string as short as possible.
§ Example Out-of-Band Message Encoding
Invitation:
{
"type": "https://didcomm.org/out-of-band/2.0/invitation",
"id": "69212a3a-d068-4f9d-a2dd-4741bca89af3",
"from": "did:example:alice",
"body": {
"goal_code": "",
"goal": ""
},
"attachments": [
{
"id": "request-0",
"media_type": "application/json",
"data": {
"json": "<json of protocol message>"
}
}
]
}
Whitespace removed:
{"type":"https://didcomm.org/out-of-band/2.0/invitation","id":"69212a3a-d068-4f9d-a2dd-4741bca89af3","from":"did:example:alice","body":{"goal_code":"","goal":""},"attachments":[{"id":"request-0","media_type":"application/json","data":{"json":"<json of protocol message>"}}]}
Base 64 URL Encoded:
eyJ0eXBlIjoiaHR0cHM6Ly9kaWRjb21tLm9yZy9vdXQtb2YtYmFuZC8yLjAvaW52aXRhdGlvbiIsImlkIjoiNjkyMTJhM2EtZDA2OC00ZjlkLWEyZGQtNDc0MWJjYTg5YWYzIiwiZnJvbSI6ImRpZDpleGFtcGxlOmFsaWNlIiwiYm9keSI6eyJnb2FsX2NvZGUiOiIiLCJnb2FsIjoiIn0sImF0dGFjaG1lbnRzIjpbeyJpZCI6InJlcXVlc3QtMCIsIm1lZGlhX3R5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwiZGF0YSI6eyJqc29uIjoiPGpzb24gb2YgcHJvdG9jb2wgbWVzc2FnZT4ifX1dfQ
Example URL:
https://example.com/path?_oob=eyJ0eXBlIjoiaHR0cHM6Ly9kaWRjb21tLm9yZy9vdXQtb2YtYmFuZC8yLjAvaW52aXRhdGlvbiIsImlkIjoiNjkyMTJhM2EtZDA2OC00ZjlkLWEyZGQtNDc0MWJjYTg5YWYzIiwiZnJvbSI6ImRpZDpleGFtcGxlOmFsaWNlIiwiYm9keSI6eyJnb2FsX2NvZGUiOiIiLCJnb2FsIjoiIn0sImF0dGFjaG1lbnRzIjpbeyJpZCI6InJlcXVlc3QtMCIsIm1lZGlhX3R5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwiZGF0YSI6eyJqc29uIjoiPGpzb24gb2YgcHJvdG9jb2wgbWVzc2FnZT4ifX1dfQ
DIDComm message URLs can be transferred via any method that can send text, including an email, SMS, posting on a website, or QR Code.
Example Email Message:
To: [email protected]
From: [email protected]
Subject: Your request to connect and receive your graduate verifiable credential
Dear Alice,
To receive your Faber College graduation certificate, click here to [connect](https://example.com/path?_oob=eyJ0eXBlIjoiaHR0cHM6Ly9kaWRjb21tLm9yZy9vdXQtb2YtYmFuZC8yLjAvaW52aXRhdGlvbiIsImlkIjoiNjkyMTJhM2EtZDA2OC00ZjlkLWEyZGQtNDc0MWJjYTg5YWYzIiwiZnJvbSI6ImRpZDpleGFtcGxlOmFsaWNlIiwiYm9keSI6eyJnb2FsX2NvZGUiOiIiLCJnb2FsIjoiIn0sImF0dGFjaG1lbnRzIjpbeyJpZCI6InJlcXVlc3QtMCIsIm1lZGlhX3R5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwiZGF0YSI6eyJqc29uIjoiPGpzb24gb2YgcHJvdG9jb2wgbWVzc2FnZT4ifX1dfQ with us, or paste the following into your browser:
https://example.com/path?_oob=eyJ0eXBlIjoiaHR0cHM6Ly9kaWRjb21tLm9yZy9vdXQtb2YtYmFuZC8yLjAvaW52aXRhdGlvbiIsImlkIjoiNjkyMTJhM2EtZDA2OC00ZjlkLWEyZGQtNDc0MWJjYTg5YWYzIiwiZnJvbSI6ImRpZDpleGFtcGxlOmFsaWNlIiwiYm9keSI6eyJnb2FsX2NvZGUiOiIiLCJnb2FsIjoiIn0sImF0dGFjaG1lbnRzIjpbeyJpZCI6InJlcXVlc3QtMCIsIm1lZGlhX3R5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwiZGF0YSI6eyJqc29uIjoiPGpzb24gb2YgcHJvdG9jb2wgbWVzc2FnZT4ifX1dfQ
If you don't have an identity agent for holding credentials, you will be given instructions on how you can get one.
Thanks,
Faber College
Knowledge is Good
Example URL encoded as a QR Code:
§ Short URL Message Retrieval
It seems inevitable that the length of some DIDComm messages will be too long to produce a useable QR code. Techniques to avoid unusable QR codes have been presented above, including using attachment links for requests, minimizing the routing of the response and eliminating unnecessary whitespace in the JSON. However, at some point a sender may need generate a very long URL. In that case, a short URL message retrieval redirection should be implemented by the sender as follows:
- The sender should generate and track a GUID for the out-of-band message URL.
- The shortened version should be:
https://example.com/path?_oobid=5f0e3ffb-3f92-4648-9868-0d6f8889e6f3
. Note the replacement of the query parameter_oob
with_oobid
when using shortened URL. - On receipt of this form of message, the agent must do an HTTP GET to retrieve the associated encoded message. A sender may want to wait to generate the full invitation until the redirection event of the shortened URL to the full length form dynamic, so a single QR code can be used for distinct messages.
A usable QR code will always be able to be generated from the shortened form of the URL.
Note: Due to the privacy implications, a standard URL shortening service SHOULD NOT be used.
§ Redirecting Back to Sender
In some cases, interaction between sender and receiver of out-of-band invitation would require receiver application to redirect back to sender.
For example,
- A web based verifier sends out-of-band invitation to a holder application and requests redirect back once present proof protocol execution is over, so that it can show credential verification results and guide the user with next steps.
- A verifier mobile application sending deep link of its mobile application to an agent based mobile wallet application requesting redirect to verifier mobile application.
These redirects may not be required in many cases, for example,
- A mobile application scanning QR code from sender and performing protocol execution. In this case the mobile application may choose to handle successful protocol execution in its own way and close the application.
§ Reference
During the protocol execution sender can securely send web_redirect
information as part of messages concluding protocol executions, like a formal acknowledgement message or a problem report.
Once protocol is ended then receiver can optionally choose to redirect by extracting the redirect information from the message.
Example acknowledgement message from verifier to prover containing web redirect information:
{
"type":"https://didcomm.org/present-proof/3.0/ack",
"id":"e2f3747b-41e8-4e46-abab-ba51472ab1c3",
"pthid":"95e63a5f-73e1-46ac-b269-48bb22591bfa",
"from":"did:example:verifier",
"to":["did:example:prover"],
"web_redirect":{
"status":"OK",
"redirectUrl":"https://example.com/handle-success/51e63a5f-93e1-46ac-b269-66bb22591bfa"
}
}
A problem report with a web redirect header from the problem report example will look like:
{
"type": "https://didcomm.org/report-problem/2.0/problem-report",
"id": "7c9de639-c51c-4d60-ab95-103fa613c805",
"pthid": "1e513ad4-48c9-444e-9e7e-5b8b45c5e325",
"web_redirect":{
"status":"FAIL",
"redirectUrl":"https://example.com/handle-error/99e80a9f-34e1-41ac-b277-91bb64481bxb"
},
"body": {
"code": "e.p.xfer.cant-use-endpoint",
"comment": "Unable to use the {1} endpoint for {2}.",
"args": [
"https://agents.r.us/inbox",
"did:sov:C805sNYhMrjHiqZDTUASHg"
]
}
}
A sender MUST use web_redirect
headers to request redirect from receiver. A web_redirect
header MUST contain status
and redirectUrl
properties.
The value of status
property MUST be one of the Acknowledgement statuses defined here which indicates protocol execution outcome.
§ Discover Features Protocol 2.0
This protocol helps agents query one another to discover which features they support, and to what extent.
The identifier for the message family used by this protocol is discover-features
, and the fully qualified PIURI for its definition is:
https://didcomm.org/discover-features/2.0
§ Motivation
Though some agents will support just one protocol and will be statically configured to interact with just one other party, many exciting uses of agents are more dynamic and unpredictable. When Alice and Bob meet, they won’t know in advance which features are supported by one another’s agents. They need a way to find out.
Disclosing features in this manner has a significant privacy benefit over endpoint disclosures contained in a DID document published to a Verifiable Data Registry (VDR). Using the single DIDComm endpoint published in the document and this protocol, features can be selectively disclosed to other parties at the owner’s discretion. The problem of anonymous scanning and fingerprinting enabled with VDR disclosures is solved in a privacy preserving way.
§ Roles
There are two roles in the discover-features
protocol: requester
and responder
. The requester asks the responder about the protocols it supports, and the responder answers. Each role uses a single message type.
It is also possible to proactively disclose features; in this case a requester receives a disclosure without asking for it. This may eliminate some chattiness in certain use cases (e.g., where two-way connectivity is limited).
§ States
This is a classic two-step request~response interaction, so it uses the predefined state machines for any requester
and responder
:
]
§ Messages
§ query
Message Type
A discover-features/query
message looks like this:
{
"type": "https://didcomm.org/discover-features/2.0/queries",
"id": "yWd8wfYzhmuXX3hmLNaV5bVbAjbWaU",
"body": {
"queries": [
{ "feature-type": "protocol", "match": "https://didcomm.org/tictactoe/1.*" },
{ "feature-type": "goal-code", "match": "org.didcomm.*" }
]
}
}
Queries messages contain one or more query objects in the queries
array. Each query essentially says, “Please tell me what features of type X you support, where the feature identifiers match this (potentially wildcarded) string.” This particular example asks an agent if it supports any 1.x versions of a tictactoe protocol, and if it supports any goal codes that begin with “org.didcomm.”.
Implementations of this protocol must recognize the following values for feature-type
: protocol
, goal-code
, and header
. Additional values of feature-type
may be used, and unrecognized values MUST be ignored.
Identifiers are used as the value to match against a feature type. Their format varies. For protocols, identifiers are PIURIs. For goal codes, identifiers are goal code values. For governance frameworks, identifiers are URIs where the framework is published. For headers, identifiers are header names.
The match
field of a query descriptor may use the * wildcard. By itself, a match
with just the wildcard says, “I’m interested in anything you want to share with me.” But usually, this wildcard will be to match a prefix that’s a little more specific, as in the example that matches any 1.x version.
Any agent may send another agent this message type at any time. Implementers of agents that intend to support dynamic relationships and rich features are strongly encouraged to implement support for this message, as it is likely to be among the first messages exchanged with a stranger.
§ disclose
Message Type
A discover-features/disclose
message looks like this:
{
"type": "https://didcomm.org/discover-features/2.0/disclose",
"thid": "yWd8wfYzhmuXX3hmLNaV5bVbAjbWaU",
"body":{
"disclosures": [
{
"feature-type": "protocol",
"id": "https://didcomm.org/tictactoe/1.0",
"roles": ["player"]
},
{
"feature-type": "goal-code",
"id": "org.didcomm.sell.goods.consumer"
}
]
}
}
The disclosures
field is a JSON array of zero or more disclosure objects that describe a feature. Each descriptor has a feature-type
field that contains data corresponding to feature-type
in a query object, and an id
field that unambiguously identifies a single item of that feature type. When the item is a protocol, the disclosure object may also contain a roles
array that enumerates the roles the responding agent can play in the associated protocol. Future feature types may add additional optional fields, though no other fields are being standardized with this version of the spec.
Disclosures messages say, “Here are some features I support (that matched your queries).”
§ Sparse Responses
Disclosures do not have to contain exhaustive detail. For example, the following response omits the optional roles
field but may be just as useful as one that includes it:
{
"type": "https://didcomm.org/discover-features/2.0/disclose",
"thid": "yWd8wfYzhmuXX3hmLNaV5bVbAjbWaU",
"body": {
"disclosures": [
{"feature-type": "protocol", "id": "https://didcomm.org/tictactoe/1.0"}
]
}
}
Less detail probably suffices because agents do not need to know everything about one another’s implementations in order to start an interaction — usually the flow will organically reveal what’s needed. For example, the outcome
message in the tictactoe
protocol isn’t needed until the end, and is optional anyway. Alice can start a tictactoe game with Bob and will eventually see whether he has the right idea about outcome
messages.
The missing roles
in this disclosure does not say, “I support no roles in this protocol.” It says, “I support the protocol but I’m providing no detail about specific roles.” Similar logic applies to any other omitted fields.
An empty disclosures
array does not say, “I support no features that match your query.” It says, “I’m not disclosing to you that I support any features (that match your query).” An agent might not tell another that it supports a feature for various reasons, including: the trust that it imputes to the other party based on cumulative interactions so far, whether it’s in the middle of upgrading a plugin, whether it’s currently under high load, and so forth. And responses to a discover-features
query are not guaranteed to be true forever; agents can be upgraded or downgraded, although they probably won’t churn in their feature profiles from moment to moment.
§ Privacy Considerations
Because the wildcards in a queries
message can be very inclusive, the discover-features
protocol could be used to mine information suitable for agent fingerprinting, in much the same way that browser fingerprinting works. This is antithetical to the ethos of our ecosystem, and represents bad behavior. Agents should use discover-features
to answer legitimate questions, and not to build detailed profiles of one another. However, fingerprinting may be attempted anyway.
For agents that want to maintain privacy, several best practices are recommended:
§ Follow selective disclosure.
Only reveal supported features based on trust in the relationship. Even if you support a protocol, you may not wish to use it in every relationship. Don’t tell others about features you do not plan to use with them.
Patterns are easier to see in larger data samples. However, a pattern of ultra-minimal data is also a problem, so use good judgment about how forthcoming to be.
§ Vary the format of responses.
Sometimes, you might prettify your agent plaintext message one way, sometimes another.
§ Vary the order of items in the disclosures
array.
If more than one key matches a query, do not always return them in alphabetical order or version order. If you do return them in order, do not always return them in ascending order.
§ Consider adding some spurious details.
If a query could match multiple features, then occasionally you might add some made-up features as matches. If a wildcard allows multiple versions of a protocol, then sometimes you might use some made-up versions. And sometimes not. (Doing this too aggressively might reveal your agent implementation, so use sparingly.)
§ Vary how you query, too.
How you ask questions may also be fingerprintable.
§ The Empty Message
Sometimes, only headers need to be communicated; there is no content for the body.
The PIURI for this protocol is:
https://didcomm.org/empty/1.0
The empty
message has no semantic meaning. The message’s only purpose is to allow the transfer of message headers.
{
"type": "https://didcomm.org/empty/1.0/empty",
"id": "518be002-de8e-456e-b3d5-8fe472477a86",
"from": "did:example:123456",
"body": {}
}
§ Trust Ping Protocol 2.0
This protocol is a standard way for agents to test connectivity,
responsiveness, and security of a DIDComm channel. It is analogous to the familiar ping
command in networking — but because it operates
over DIDComm, it is transport agnostic and asynchronous, and it can produce insights into privacy and security that a regular ping cannot.
The PIURI for this protocol is:
https://didcomm.org/trust-ping/2.0
§ Roles
There are two parties in a trust ping: the sender
and the receiver
. The sender initiates the trust
ping. The receiver responds. If the receiver wants
to do a ping of their own, they can, but this is a
new interaction in which they become the sender.
§ Messages
§ ping
The trust ping interaction begins when sender
creates a ping
message like this:
{
"type": "https://didcomm.org/trust-ping/2.0/ping",
"id": "518be002-de8e-456e-b3d5-8fe472477a86",
"from": "did:example:123456",
"body": {
"response_requested": true
}
}
response_requested: default value is true
. If false, the sender
is not requesting a ping-response
from the receiver
. If true
, the sender
is requesting a response.
§ ping-response
When the message arrives at the receiver, assuming that response_requested
is not false
, the receiver should reply as quickly as possible with a
ping-response
message that looks like this:
{
"type": "https://didcomm.org/trust-ping/2.0/ping-response",
"id": "e002518b-456e-b3d5-de8e-7a86fe472847",
"thid": "518be002-de8e-456e-b3d5-8fe472477a86"
}
§ Trust
This is the “trust ping protocol”, not just the “ping protocol.” The “trust” in its name comes from several features that the interaction gains by virtue of the properties of the DIDComm messages. A ping and response verify to both parties that the necessary encryption is in place and working properly for the messages to be understood.
§ Internationalization (i18n)
Because automation makes life easier for humans, the data in DIDComm messages is usually assumed to have software as its audience. However, sometimes humans should see part of the data in a DIDComm message. For example, if the high-level application protocol running atop DIDComm is a kind of rich chat, humans may not see message headers and the details of threads — but they will want to read the text sent to them by a friend. Similarly, humans may need to read error messages or terms and conditions in their natural language.
DIDComm offers simple i18n features to address this need. They are intended to impose no up-front design burden on protocol implementers; multi-language support can be added once a protocol has adoption, with very little effort. These features also degrade gracefully and without coordination. Any party can introduce them to an interaction, but if others do not understand or support them, or if parties to the protocol have no human language in common, the interaction is typically still viable. (A protocol that inherently requires multilanguage support — e.g., to provides close captioning in second language — is the only exception.)
§ Internationalized by default
The default assumption about every field in a DIDComm message is that it is locale-independent (internationalized) already. Since number representation is governed by JSON syntax, and dates are represented as seconds-since-epoch or as ISO/IETF 3339 strings, this assumption is automatically true for data types that are not strings.
All string values in DIDComm messages are encoded as UTF-8, which is capable of representing the full Unicode character inventory. However, the default assumption for every string field in DIDComm also MUST be that it is locale-independent. This is appropriate for headers like id
and type
, for fields that contain URIs, and so forth.
§ accept-lang
header
For string values that are language-specific, any party in a DIDComm interaction MAY declare the human languages that they prefer by using the accept-lang
header. This allows those who send them messages to localize the content appropriately.
The value of this header is an array of IANA’s language codes, ranked from most to least preferred. Once a language preference has been set, it MUST be assumed to apply until it is changed, or for the duration of an application-level protocol instance (a DIDComm thread) — whichever comes first. Parties who see this header MAY assume it is an appropriate default for future interactions as well. However, they MUST NOT apply the assumption to any other interactions that are already underway, as this would allow one protocol to trigger unpredictable side effects in another.
§ lang
header
When a sender is preparing a message that contains language-specific fields, they SHOULD clarify how to interpret those fields by using the lang
header. This is a general best practices. Individual protocols that have strong dependencies on human language MAY require this header in contexts they govern.
Ideally, the value of lang
will derive from a previously-seen accept-lang
header, reflecting the fact that the sender is communicating in a language that the recipient prefers. (The sender could get matching language content by looking it up in message tables, calling a machine translation service, or — if the content is generated dynamically by a human — simply asking the human sender to speak or write in the target language.) However, even when no match is achieved, declaring lang
lets the recipient call a machine translation service or take other intelligent action.
This header works much like lang
in HTML, and its value comes from IANA’s language subtag registry. If this header is present, then any string field inside body
that contains human-readable text (according to the active protocol’s definition of the message type) MUST hold text in the identified language.
§ i18n example
Suppose a chess protocol allows players to include human-friendly comments with their moves. At the beginning of the chess game, Bob includes a message that contains this header:
"accept-lang": ["fr", "en"]
This tells Alice that Bob prefers to interact in French, with English as a backup preference.
When Alice puts Bob in checkmate, assuming she has the desire and ability to honor Bob’s preference, her message might look like this:
{
"id": "388d599a-fdc1-4890-b32a-be6cd3893564",
"type": "https://didcomm.org/chess/1.0/move",
"lang": "fr",
"body": {
"move": "BC4#",
"comment": "C'est échec et mat, mon pote."
}
}
On the other hand, if Alice is unable to send French text, her message might contain "lang": "en"
and "comment": "That's checkmate, buddy."
Even though this comment is not in the language Bob prefers, at least Bob knows what language it is in.
§ Asking for a different lang
What if Alice doesn’t support any of the languages in Bob’s accept-lang
header — or if Bob never used such a header in the first place?
Bob MAY tell Alice that the language she used is problematic by sending her a problem report where the code
field is w.msg.bad-lang
. (In some protocols where language-specific fields may be vital rather than incidental, a problem like this might be an error instead of a warning; in such cases, the code MUST be e.msg.bad-lang
instead.) Bob may include an accept-lang
header on this problem-report
message, teaching Alice what it will take to fix the problem.
§ Advanced i18n patterns
When protocols have i18n needs that are more advanced than this, a DIDComm extension such as the l10n extension is recommended.
§ Future-Proofing
§ Versioning
This version of the standard is known as “DIDComm v2” — acknowledging the fact that a v1 generation of DIDComm specs was incubated under the Hyperledger Aries project umbrella. The v1 specs are close conceptual cousins, but use a slightly different encryption envelope, and base their plaintext format on arbitrary JSON instead of JWMs.
Future evolutions of the spec will follow semver conventions. Minor updates that add features without breaking compatibility will carry a minor version number update: 2.1, 2.2, and so forth. Breaking changes will prompt a major version change.
§ Extensions
The general mechanism for DIDComm extensibility is the development of DIDComm protocols. In the case where extensibility requires a modification to the base DIDComm spec itself, a DIDComm extension is to be used. An extension adds a self-contained set of conventions or features. Support for DIDComm extensions is optional.
Each DIDComm extension is described in a specification of its own. Software that implements a DIDComm Extension in addition to the DIDComm spec will indicate so via link to the extension spec.
§ Future Work
§ Additional Encodings
DIDComm messages are JSON encoded (based on the JOSE family of specs) at the encryption, signature, and content level. Future encodings might introduce binary serializations. Each innovation like this MUST specify a deterministic and reliable method for indicating the alternative encoding used.
At multiple points in the creation of this spec the community discussed switching to CBOR as a primary encoding format to replace JSON. Two reasons prevented that switch: The maturity of the JSON related standards as compared to CBOR related standards, and the cost of switching so late in spec development.
§ Beyond Messaging
This is a DIDComm messaging spec. Security, privacy, routing, and metadata concepts from this spec could be adapted to other communication styles, including multicast/broadcast and streaming. This will create sister specs to DIDComm Messaging, rather than evolving DIDComm Messaging itself.
§ Post-Quantum Crypto
The designers of DIDComm are aware that DIDComm’s cryptographic methods will need to be upgraded when quantum computing matures. This is because DIDComm makes heavy use of asymmetric elliptic curve mechanisms that depend on the discrete logarithm problem; this computational hardness is known to be vulnerable to a quantum computer that can run Shor’s algorithm. Similar risks will drive upgrades to TLS, Ethereum, Bitcoin, and many other systems that are considered highly secure today.
Some modest preparations for quantum-resistant DIDComm have already begun. DIDComm is able to use arbitrary DID methods, which should allow approaches that are quantum-secure without changing the general pattern of DIDComm’s interaction with key management technology.
Libraries that provide quantum-resistant algorithms for signing and encryption are now available, but research is needed to determine which approaches are worthy of broad adoption. This is the subject of an ongoing project sponsored by NIST, and of a similar project in the EU.
We expect to update the DIDComm Messaging spec as these projects release mature recommendations and the cryptographic libraries they vet achieve adoption.
§ Appendix
This section provides test vectors of DIDComm messages. The test vectors defined in the Appendix C might be used to validate implementations of DIDComm Messaging. The test vectors use the sender and recipient private keys defined in the Appendix A to decrypt and sign DIDComm messages. The test vectors use the sender and recipient public keys defined in the Appendix B to encrypt DIDComm Encrypted Messages and verify signature of DIDComm Signed Messages.
§ Appendix A. Secrets for Test Vectors
This section provides the sender and recipient private keys. These keys might be used for decryption and signing DIDComm messages.
§ A.1. Sender Secrets
This section defines the sender private keys in JWK format.
[
{
"kid":"did:example:alice#key-1",
"kty":"OKP",
"d":"pFRUKkyzx4kHdJtFSnlPA9WzqkDT1HWV0xZ5OYZd2SY",
"crv":"Ed25519",
"x":"G-boxFB6vOZBu-wXkm-9Lh79I8nf9Z50cILaOgKKGww"
},
{
"kid":"did:example:alice#key-2",
"kty":"EC",
"d":"7TCIdt1rhThFtWcEiLnk_COEjh1ZfQhM4bW2wz-dp4A",
"crv":"P-256",
"x":"2syLh57B-dGpa0F8p1JrO6JU7UUSF6j7qL-vfk1eOoY",
"y":"BgsGtI7UPsObMRjdElxLOrgAO9JggNMjOcfzEPox18w"
},
{
"kid":"did:example:alice#key-3",
"kty":"EC",
"d":"N3Hm1LXA210YVGGsXw_GklMwcLu_bMgnzDese6YQIyA",
"crv":"secp256k1",
"x":"aToW5EaTq5mlAf8C5ECYDSkqsJycrW-e1SQ6_GJcAOk",
"y":"JAGX94caA21WKreXwYUaOCYTBMrqaX4KWIlsQZTHWCk"
},
{
"kid":"did:example:alice#key-x25519-1",
"kty":"OKP",
"d":"r-jK2cO3taR8LQnJB1_ikLBTAnOtShJOsHXRUWT-aZA",
"crv":"X25519",
"x":"avH0O2Y4tqLAq8y9zpianr8ajii5m4F_mICrzNlatXs"
},
{
"kid":"did:example:alice#key-p256-1",
"kty":"EC",
"d":"sB0bYtpaXyp-h17dDpMx91N3Du1AdN4z1FUq02GbmLw",
"crv":"P-256",
"x":"L0crjMN1g0Ih4sYAJ_nGoHUck2cloltUpUVQDhF2nHE",
"y":"SxYgE7CmEJYi7IDhgK5jI4ZiajO8jPRZDldVhqFpYoo"
},
{
"kid":"did:example:alice#key-p521-1",
"kty":"EC",
"d":"AQCQKE7rZpxPnX9RgjXxeywrAMp1fJsyFe4cir1gWj-8t8xWaM_E2qBkTTzyjbRBu-JPXHe_auT850iYmE34SkWi",
"crv":"P-521",
"x":"AHBEVPRhAv-WHDEvxVM9S0px9WxxwHL641Pemgk9sDdxvli9VpKCBdra5gg_4kupBDhz__AlaBgKOC_15J2Byptz",
"y":"AciGcHJCD_yMikQvlmqpkBbVqqbg93mMVcgvXBYAQPP-u9AF7adybwZrNfHWCKAQwGF9ugd0Zhg7mLMEszIONFRk"
}
]
§ A.2. Recipient Secrets
This section defines the recipient private keys in JWK format.
[
{
"kid ":"did:example:bob#key-x25519-1",
"kty":"OKP",
"d":"b9NnuOCB0hm7YGNvaE9DMhwH_wjZA1-gWD6dA0JWdL0",
"crv":"X25519",
"x":"GDTrI66K0pFfO54tlCSvfjjNapIs44dzpneBgyx0S3E"
},
{
"kid ":"did:example:bob#key-x25519-2",
"kty":"OKP",
"d":"p-vteoF1gopny1HXywt76xz_uC83UUmrgszsI-ThBKk",
"crv":"X25519",
"x":"UT9S3F5ep16KSNBBShU2wh3qSfqYjlasZimn0mB8_VM"
},
{
"kid ":"did:example:bob#key-x25519-3",
"kty":"OKP",
"d":"f9WJeuQXEItkGM8shN4dqFr5fLQLBasHnWZ-8dPaSo0",
"crv":"X25519",
"x":"82k2BTUiywKv49fKLZa-WwDi8RBf0tB0M8bvSAUQ3yY"
},
{
"kid ":"did:example:bob#key-p256-1",
"kty":"EC",
"d":"PgwHnlXxt8pwR6OCTUwwWx-P51BiLkFZyqHzquKddXQ",
"crv":"P-256",
"x":"FQVaTOksf-XsCUrt4J1L2UGvtWaDwpboVlqbKBY2AIo",
"y":"6XFB9PYo7dyC5ViJSO9uXNYkxTJWn0d_mqJ__ZYhcNY"
},
{
"kid ":"did:example:bob#key-p256-2",
"kty":"EC",
"d":"agKz7HS8mIwqO40Q2dwm_Zi70IdYFtonN5sZecQoxYU",
"crv":"P-256",
"x":"n0yBsGrwGZup9ywKhzD4KoORGicilzIUyfcXb1CSwe0",
"y":"ov0buZJ8GHzV128jmCw1CaFbajZoFFmiJDbMrceCXIw"
},
{
"kid ":"did:example:bob#key-p384-1",
"kty":"EC",
"d":"ajqcWbYA0UDBKfAhkSkeiVjMMt8l-5rcknvEv9t_Os6M8s-HisdywvNCX4CGd_xY",
"crv":"P-384",
"x":"MvnE_OwKoTcJVfHyTX-DLSRhhNwlu5LNoQ5UWD9Jmgtdxp_kpjsMuTTBnxg5RF_Y",
"y":"X_3HJBcKFQEG35PZbEOBn8u9_z8V1F9V1Kv-Vh0aSzmH-y9aOuDJUE3D4Hvmi5l7"
},
{
"kid ":"did:example:bob#key-p384-2",
"kty":"EC",
"d":"OiwhRotK188BtbQy0XBO8PljSKYI6CCD-nE_ZUzK7o81tk3imDOuQ-jrSWaIkI-T",
"crv":"P-384",
"x":"2x3HOTvR8e-Tu6U4UqMd1wUWsNXMD0RgIunZTMcZsS-zWOwDgsrhYVHmv3k_DjV3",
"y":"W9LLaBjlWYcXUxOf6ECSfcXKaC3-K9z4hCoP0PS87Q_4ExMgIwxVCXUEB6nf0GDd"
},
{
"kid ":"did:example:bob#key-p521-1",
"kty":"EC",
"d":"AV5ocjvy7PkPgNrSuvCxtG70NMj6iTabvvjSLbsdd8OdI9HlXYlFR7RdBbgLUTruvaIRhjEAE9gNTH6rWUIdfuj6",
"crv":"P-521",
"x":"Af9O5THFENlqQbh2Ehipt1Yf4gAd9RCa3QzPktfcgUIFADMc4kAaYVViTaDOuvVS2vMS1KZe0D5kXedSXPQ3QbHi",
"y":"ATZVigRQ7UdGsQ9j-omyff6JIeeUv3CBWYsZ0l6x3C_SYqhqVV7dEG-TafCCNiIxs8qeUiXQ8cHWVclqkH4Lo1qH"
},
{
"kid ":"did:example:bob#key-p521-2",
"kty":"EC",
"d":"ABixMEZHsyT7SRw-lY5HxdNOofTZLlwBHwPEJ3spEMC2sWN1RZQylZuvoyOBGJnPxg4-H_iVhNWf_OtgYODrYhCk",
"crv":"P-521",
"x":"ATp_WxCfIK_SriBoStmA0QrJc2pUR1djpen0VdpmogtnKxJbitiPq-HJXYXDKriXfVnkrl2i952MsIOMfD2j0Ots",
"y":"AEJipR0Dc-aBZYDqN51SKHYSWs9hM58SmRY1MxgXANgZrPaq1EeGMGOjkbLMEJtBThdjXhkS5VlXMkF0cYhZELiH"
}
]
§ Appendix B. DIDDocs for Test Vectors
This section provides DIDDocs for the sender and recipient. The following DIDDocs might be used to validate implementations of DIDComm Messaging. They are also intended to test vectors defined in the Appendix C.
§ B.1. Sender DIDDocs
This section defines the sender DIDDoc.
{
"@context":[
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/jws-2020/v1"
],
"id":"did:example:alice",
"authentication":[
{
"id":"did:example:alice#key-1",
"type":"JsonWebKey2020",
"controller":"did:example:alice#key-1",
"publicKeyJwk":{
"kty":"OKP",
"crv":"Ed25519",
"x":"G-boxFB6vOZBu-wXkm-9Lh79I8nf9Z50cILaOgKKGww"
}
},
{
"id":"did:example:alice#key-2",
"type":"JsonWebKey2020",
"controller":"did:example:alice#key-2",
"publicKeyJwk":{
"kty":"EC",
"crv":"P-256",
"x":"2syLh57B-dGpa0F8p1JrO6JU7UUSF6j7qL-vfk1eOoY",
"y":"BgsGtI7UPsObMRjdElxLOrgAO9JggNMjOcfzEPox18w"
}
},
{
"id":"did:example:alice#key-3",
"type":"JsonWebKey2020",
"controller":"did:example:alice#key-3",
"publicKeyJwk":{
"kty":"EC",
"crv":"secp256k1",
"x":"aToW5EaTq5mlAf8C5ECYDSkqsJycrW-e1SQ6_GJcAOk",
"y":"JAGX94caA21WKreXwYUaOCYTBMrqaX4KWIlsQZTHWCk"
}
}
],
"keyAgreement":[
{
"id":"did:example:alice#key-x25519-1",
"type":"JsonWebKey2020",
"controller":"did:example:alice#key-x25519-1",
"publicKeyJwk":{
"kty":"OKP",
"crv":"X25519",
"x":"avH0O2Y4tqLAq8y9zpianr8ajii5m4F_mICrzNlatXs"
}
},
{
"id":"did:example:alice#key-p256-1",
"type":"JsonWebKey2020",
"controller":"did:example:alice#key-p256-1",
"publicKeyJwk":{
"kty":"EC",
"crv":"P-256",
"x":"L0crjMN1g0Ih4sYAJ_nGoHUck2cloltUpUVQDhF2nHE",
"y":"SxYgE7CmEJYi7IDhgK5jI4ZiajO8jPRZDldVhqFpYoo"
}
},
{
"id":"did:example:alice#key-p521-1",
"type":"JsonWebKey2020",
"controller":"did:example:alice#key-p521-1",
"publicKeyJwk":{
"kty":"EC",
"crv":"P-521",
"x":"AHBEVPRhAv-WHDEvxVM9S0px9WxxwHL641Pemgk9sDdxvli9VpKCBdra5gg_4kupBDhz__AlaBgKOC_15J2Byptz",
"y":"AciGcHJCD_yMikQvlmqpkBbVqqbg93mMVcgvXBYAQPP-u9AF7adybwZrNfHWCKAQwGF9ugd0Zhg7mLMEszIONFRk"
}
}
]
}
§ B.2. Recipient DIDDocs
This section defines the recipient DIDDoc.
{
"@context":[
"https://www.w3.org/ns/did/v2"
],
"id":"did:example:bob",
"keyAgreement":[
{
"id":"did:example:bob#key-x25519-1",
"type":"JsonWebKey2020",
"controller":"did:example:bob#key-x25519-1",
"publicKeyJwk":{
"kty":"OKP",
"crv":"X25519",
"x":"GDTrI66K0pFfO54tlCSvfjjNapIs44dzpneBgyx0S3E"
}
},
{
"id":"did:example:bob#key-x25519-2",
"type":"JsonWebKey2020",
"controller":"did:example:bob#key-x25519-2",
"publicKeyJwk":{
"kty":"OKP",
"crv":"X25519",
"x":"UT9S3F5ep16KSNBBShU2wh3qSfqYjlasZimn0mB8_VM"
}
},
{
"id":"did:example:bob#key-x25519-3",
"type":"JsonWebKey2020",
"controller":"did:example:bob#key-x25519-3",
"publicKeyJwk":{
"kty":"OKP",
"crv":"X25519",
"x":"82k2BTUiywKv49fKLZa-WwDi8RBf0tB0M8bvSAUQ3yY"
}
},
{
"id":"did:example:bob#key-p256-1",
"type":"JsonWebKey2020",
"controller":"did:example:bob#key-p256-1",
"publicKeyJwk":{
"kty":"EC",
"crv":"P-256",
"x":"FQVaTOksf-XsCUrt4J1L2UGvtWaDwpboVlqbKBY2AIo",
"y":"6XFB9PYo7dyC5ViJSO9uXNYkxTJWn0d_mqJ__ZYhcNY"
}
},
{
"id":"did:example:bob#key-p256-2",
"type":"JsonWebKey2020",
"controller":"did:example:bob#key-p256-2",
"publicKeyJwk":{
"kty":"EC",
"crv":"P-256",
"x":"n0yBsGrwGZup9ywKhzD4KoORGicilzIUyfcXb1CSwe0",
"y":"ov0buZJ8GHzV128jmCw1CaFbajZoFFmiJDbMrceCXIw"
}
},
{
"id":"did:example:bob#key-p384-1",
"type":"JsonWebKey2020",
"controller":"did:example:bob#key-p384-1",
"publicKeyJwk":{
"kty":"EC",
"crv":"P-384",
"x":"MvnE_OwKoTcJVfHyTX-DLSRhhNwlu5LNoQ5UWD9Jmgtdxp_kpjsMuTTBnxg5RF_Y",
"y":"X_3HJBcKFQEG35PZbEOBn8u9_z8V1F9V1Kv-Vh0aSzmH-y9aOuDJUE3D4Hvmi5l7"
}
},
{
"id":"did:example:bob#key-p384-2",
"type":"JsonWebKey2020",
"controller":"did:example:bob#key-p384-2",
"publicKeyJwk":{
"kty":"EC",
"crv":"P-384",
"x":"2x3HOTvR8e-Tu6U4UqMd1wUWsNXMD0RgIunZTMcZsS-zWOwDgsrhYVHmv3k_DjV3",
"y":"W9LLaBjlWYcXUxOf6ECSfcXKaC3-K9z4hCoP0PS87Q_4ExMgIwxVCXUEB6nf0GDd"
}
},
{
"id":"did:example:bob#key-p521-1",
"type":"JsonWebKey2020",
"controller":"did:example:bob#key-p521-1",
"publicKeyJwk":{
"kty":"EC",
"crv":"P-521",
"x":"Af9O5THFENlqQbh2Ehipt1Yf4gAd9RCa3QzPktfcgUIFADMc4kAaYVViTaDOuvVS2vMS1KZe0D5kXedSXPQ3QbHi",
"y":"ATZVigRQ7UdGsQ9j-omyff6JIeeUv3CBWYsZ0l6x3C_SYqhqVV7dEG-TafCCNiIxs8qeUiXQ8cHWVclqkH4Lo1qH"
}
},
{
"id":"did:example:bob#key-p521-2",
"type":"JsonWebKey2020",
"controller":"did:example:bob#key-p521-2",
"publicKeyJwk":{
"kty":"EC",
"crv":"P-521",
"x":"ATp_WxCfIK_SriBoStmA0QrJc2pUR1djpen0VdpmogtnKxJbitiPq-HJXYXDKriXfVnkrl2i952MsIOMfD2j0Ots",
"y":"AEJipR0Dc-aBZYDqN51SKHYSWs9hM58SmRY1MxgXANgZrPaq1EeGMGOjkbLMEJtBThdjXhkS5VlXMkF0cYhZELiH"
}
}
]
}
§ Appendix C. Test Vectors
This section provides the test vectors. The following the test vectors might be used to validate implementations of DIDComm Messaging. The test vectors use private keys defined in the Appendix A and public keys defined in the Appendix B.
§ C.1. DIDComm Plaintext Messages
The following example defines DIDComm Plaintext message. The message is used for DIDComm Signed Messages and DIDComm Encrypted Messages.
{
"id":"1234567890",
"type":"https://example.com/protocols/lets_do_lunch/1.0/proposal",
"from":"did:example:alice",
"to":[
"did:example:bob"
],
"created_time":1516269022,
"expires_time":1516385931,
"body":{
"messagespecificattribute":"and its value"
}
}
§ C.2 DIDComm Signed Messages
This section provides examples for DIDComm Signed Messages. Examples sign DIDComm Plaintext Message.
This example uses EdDSA digital signature with a curve Ed25519.
{
"payload":"eyJpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXBsYWluK2pzb24iLCJ0eXBlIjoiaHR0cDovL2V4YW1wbGUuY29tL3Byb3RvY29scy9sZXRzX2RvX2x1bmNoLzEuMC9wcm9wb3NhbCIsImZyb20iOiJkaWQ6ZXhhbXBsZTphbGljZSIsInRvIjpbImRpZDpleGFtcGxlOmJvYiJdLCJjcmVhdGVkX3RpbWUiOjE1MTYyNjkwMjIsImV4cGlyZXNfdGltZSI6MTUxNjM4NTkzMSwiYm9keSI6eyJtZXNzYWdlc3BlY2lmaWNhdHRyaWJ1dGUiOiJhbmQgaXRzIHZhbHVlIn19",
"signatures":[
{
"protected":"eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXNpZ25lZCtqc29uIiwiYWxnIjoiRWREU0EifQ",
"signature":"FW33NnvOHV0Ted9-F7GZbkia-vYAfBKtH4oBxbrttWAhBZ6UFJMxcGjL3lwOl4YohI3kyyd08LHPWNMgP2EVCQ",
"header":{
"kid":"did:example:alice#key-1"
}
}
]
}
This example uses ES256 digital signature.
{
"payload":"eyJpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXBsYWluK2pzb24iLCJ0eXBlIjoiaHR0cDovL2V4YW1wbGUuY29tL3Byb3RvY29scy9sZXRzX2RvX2x1bmNoLzEuMC9wcm9wb3NhbCIsImZyb20iOiJkaWQ6ZXhhbXBsZTphbGljZSIsInRvIjpbImRpZDpleGFtcGxlOmJvYiJdLCJjcmVhdGVkX3RpbWUiOjE1MTYyNjkwMjIsImV4cGlyZXNfdGltZSI6MTUxNjM4NTkzMSwiYm9keSI6eyJtZXNzYWdlc3BlY2lmaWNhdHRyaWJ1dGUiOiJhbmQgaXRzIHZhbHVlIn19",
"signatures":[
{
"protected":"eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXNpZ25lZCtqc29uIiwiYWxnIjoiRVMyNTYifQ",
"signature":"gcW3lVifhyR48mLHbbpnGZQuziskR5-wXf6IoBlpa9SzERfSG9I7oQ9pssmHZwbvJvyMvxskpH5oudw1W3X5Qg",
"header":{
"kid":"did:example:alice#key-2"
}
}
]
}
This example uses ES256K digital signature.
{
"payload":"eyJpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXBsYWluK2pzb24iLCJ0eXBlIjoiaHR0cDovL2V4YW1wbGUuY29tL3Byb3RvY29scy9sZXRzX2RvX2x1bmNoLzEuMC9wcm9wb3NhbCIsImZyb20iOiJkaWQ6ZXhhbXBsZTphbGljZSIsInRvIjpbImRpZDpleGFtcGxlOmJvYiJdLCJjcmVhdGVkX3RpbWUiOjE1MTYyNjkwMjIsImV4cGlyZXNfdGltZSI6MTUxNjM4NTkzMSwiYm9keSI6eyJtZXNzYWdlc3BlY2lmaWNhdHRyaWJ1dGUiOiJhbmQgaXRzIHZhbHVlIn19",
"signatures":[
{
"protected":"eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXNpZ25lZCtqc29uIiwiYWxnIjoiRVMyNTZLIn0",
"signature":"EGjhIcts6tqiJgqtxaTiTY3EUvL-_rLjn9lxaZ4eRUwa1-CS1nknZoyJWbyY5NQnUafWh5nvCtQpdpMyzH3blw",
"header":{
"kid":"did:example:alice#key-3"
}
}
]
}
§ C.3. DIDComm Encrypted Messages
This section provides examples for DIDComm Encrypted Messages. Examples encrypt the DIDComm Plaintext Message.
This example uses ECDH-ES key wrapping algorithm using key with X25519 elliptic curve and XC20P for content encryption of the message.
{
"ciphertext":"KWS7gJU7TbyJlcT9dPkCw-ohNigGaHSukR9MUqFM0THbCTCNkY-g5tahBFyszlKIKXs7qOtqzYyWbPou2q77XlAeYs93IhF6NvaIjyNqYklvj-OtJt9W2Pj5CLOMdsR0C30wchGoXd6wEQZY4ttbzpxYznqPmJ0b9KW6ZP-l4_DSRYe9B-1oSWMNmqMPwluKbtguC-riy356Xbu2C9ShfWmpmjz1HyJWQhZfczuwkWWlE63g26FMskIZZd_jGpEhPFHKUXCFwbuiw_Iy3R0BIzmXXdK_w7PZMMPbaxssl2UeJmLQgCAP8j8TukxV96EKa6rGgULvlo7qibjJqsS5j03bnbxkuxwbfyu3OxwgVzFWlyHbUH6p",
"protected":"eyJlcGsiOnsia3R5IjoiT0tQIiwiY3J2IjoiWDI1NTE5IiwieCI6IkpIanNtSVJaQWFCMHpSR193TlhMVjJyUGdnRjAwaGRIYlc1cmo4ZzBJMjQifSwiYXB2IjoiTmNzdUFuclJmUEs2OUEtcmtaMEw5WFdVRzRqTXZOQzNaZzc0QlB6NTNQQSIsInR5cCI6ImFwcGxpY2F0aW9uL2RpZGNvbW0tZW5jcnlwdGVkK2pzb24iLCJlbmMiOiJYQzIwUCIsImFsZyI6IkVDREgtRVMrQTI1NktXIn0",
"recipients":[
{
"encrypted_key":"3n1olyBR3nY7ZGAprOx-b7wYAKza6cvOYjNwVg3miTnbLwPP_FmE1A",
"header":{
"kid":"did:example:bob#key-x25519-1"
}
},
{
"encrypted_key":"j5eSzn3kCrIkhQAWPnEwrFPMW6hG0zF_y37gUvvc5gvlzsuNX4hXrQ",
"header":{
"kid":"did:example:bob#key-x25519-2"
}
},
{
"encrypted_key":"TEWlqlq-ao7Lbynf0oZYhxs7ZB39SUWBCK4qjqQqfeItfwmNyDm73A",
"header":{
"kid":"did:example:bob#key-x25519-3"
}
}
],
"tag":"6ylC_iAs4JvDQzXeY6MuYQ",
"iv":"ESpmcyGiZpRjc5urDela21TOOTW8Wqd1"
}
This example uses ECDH-ES key wrapping algorithm using key with NIST defined P-384 elliptic curve and A256CBC-HS512 for content encryption of the message.
{
"ciphertext":"HPnc9w7jK0T73Spifq_dcVJnONbT9MZ9oorDJFEBJAfmwYRqvs1rKue-udrNLTTH0qjjbeuji01xPRF5JiWyy-gSMX4LHdLhPxHxjjQCTkThY0kapofU85EjLPlI4ytbHiGcrPIezqCun4iDkmb50pwiLvL7XY1Ht6zPUUdhiV6qWoPP4qeY_8pfH74Q5u7K4TQ0uU3KP8CVZQuafrkOBbqbqpJV-lWpWIKxil44f1IT_GeIpkWvmkYxTa1MxpYBgOYa5_AUxYBumcIFP-b6g7GQUbN-1SOoP76EzxZU_louspzQ2HdEH1TzXw2LKclN8GdxD7kB0H6lZbZLT3ScDzSVSbvO1w1fXHXOeOzywuAcismmoEXQGbWZm7wJJJ2r",
"protected":"eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTM4NCIsIngiOiIxNjFhZ0dlYWhHZW1IZ25qSG1RX0JfU09OeUJWZzhWTGRoVGdWNVc1NFZiYWJ5bGxpc3NuWjZXNzc5SW9VcUtyIiwieSI6ImNDZXFlRmdvYm9fY1ItWTRUc1pCWlg4dTNCa2l5TnMyYi12ZHFPcU9MeUNuVmdPMmpvN25zQV9JQzNhbnQ5T1gifSwiYXB2IjoiTEpBOUVva3M1dGFtVUZWQmFsTXdCaEo2RGtEY0o4SEs0U2xYWldxRHFubyIsInR5cCI6ImFwcGxpY2F0aW9uL2RpZGNvbW0tZW5jcnlwdGVkK2pzb24iLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiRUNESC1FUytBMjU2S1cifQ",
"recipients":[
{
"encrypted_key":"SlyWCiOaHMMH9CqSs2CHpRd2XwbueZ1-MfYgKVepXWpgmTgtsgNOAaYwV5pxK3D67HV51F-vLBFlAHke7RYp_GeGDFYhAf5s",
"header":{
"kid":"did:example:bob#key-p384-1"
}
},
{
"encrypted_key":"5e7ChtaRgIlV4yS4NSD7kEo0iJfFmL_BFgRh3clDKBG_QoPd1eOtFlTxFJh-spE0khoaw8vEEYTcQIg4ReeFT3uQ8aayz1oY",
"header":{
"kid":"did:example:bob#key-p384-2"
}
}
],
"tag":"bkodXkuuwRbqksnQNsCM2YLy9f0v0xNgnhSUAoFGtmE",
"iv":"aE1XaH767m7LY0JTN7RsAA"
}
This example uses ECDH-ES key wrapping algorithm using key with NIST defined P-521 elliptic curve and A256GCM for content encryption of the message.
{
"ciphertext":"mxnFl4s8FRsIJIBVcRLv4gj4ru5R0H3BdvyBWwXV3ILhtl_moqzx9COINGomP4ueuApuY5xdMDvRHm2mLo6N-763wjNSjAibNrqVZC-EG24jjYk7RPZ26fEW4z87LHuLTicYCD4yHqilRbRgbOCT0Db5221Kec0HDZTXLzBqVwC2UMyDF4QT6Uz3fE4f_6BXTwjD-sEgM67wWTiWbDJ3Q6WyaOL3W4ukYANDuAR05-SXVehnd3WR0FOg1hVcNRao5ekyWZw4Z2ekEB1JRof3Lh6uq46K0KXpe9Pc64UzAxEID93SoJ0EaV_Sei8CXw2aJFmZUuCf8YISWKUz6QZxRvFKUfYeflldUm9U2tY96RicWgUhuXgv",
"protected":"eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTUyMSIsIngiOiJBRWtrc09abW1oZkZYdU90MHMybVdFYlVybVQ3OXc1SFRwUm9TLTZZNXpkYlk5T0I5b2RHb2hDYm1PeGpqY2VhWUU5ZnNaX3RaNmdpTGFBNUFEUnBrWE5VIiwieSI6IkFDaWJnLXZEMmFHVEpHbzlmRUl6Q1dXT2hSVUlObFg3Q1hGSTJqeDlKVDZmTzJfMGZ3SzM2WTctNHNUZTRpRVVSaHlnU1hQOW9TVFczTkdZTXVDMWlPQ3AifSwiYXB2IjoiR09lbzc2eW02TkNnOVdXTUVZZlcwZVZEVDU2Njh6RWhsMnVBSVctRS1IRSIsInR5cCI6ImFwcGxpY2F0aW9uL2RpZGNvbW0tZW5jcnlwdGVkK2pzb24iLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiRUNESC1FUytBMjU2S1cifQ",
"recipients":[
{
"encrypted_key":"W4KOy5W88iPPsDEdhkJN2krZ2QAeDxOIxW-4B21H9q89SHWexocCrw",
"header":{
"kid":"did:example:bob#key-p521-1"
}
},
{
"encrypted_key":"uxKPkF6-sIiEkdeJcUPJY4lvsRg_bvtLPIn7eIycxLJML2KM6-Llag",
"header":{
"kid":"did:example:bob#key-p521-2"
}
}
],
"tag":"aPZeYfwht2Nx9mfURv3j3g",
"iv":"lGKCvg2xrvi8Qa_D"
}
This example uses ECDH-1PU key wrapping algorithm using key with X25519 elliptic curve and A256CBC-HS512 for content encryption of the message.
{
"ciphertext":"MJezmxJ8DzUB01rMjiW6JViSaUhsZBhMvYtezkhmwts1qXWtDB63i4-FHZP6cJSyCI7eU-gqH8lBXO_UVuviWIqnIUrTRLaumanZ4q1dNKAnxNL-dHmb3coOqSvy3ZZn6W17lsVudjw7hUUpMbeMbQ5W8GokK9ZCGaaWnqAzd1ZcuGXDuemWeA8BerQsfQw_IQm-aUKancldedHSGrOjVWgozVL97MH966j3i9CJc3k9jS9xDuE0owoWVZa7SxTmhl1PDetmzLnYIIIt-peJtNYGdpd-FcYxIFycQNRUoFEr77h4GBTLbC-vqbQHJC1vW4O2LEKhnhOAVlGyDYkNbA4DSL-LMwKxenQXRARsKSIMn7z-ZIqTE-VCNj9vbtgR",
"protected":"eyJlcGsiOnsia3R5IjoiT0tQIiwiY3J2IjoiWDI1NTE5IiwieCI6IkdGY01vcEpsamY0cExaZmNoNGFfR2hUTV9ZQWY2aU5JMWRXREd5VkNhdzAifSwiYXB2IjoiTmNzdUFuclJmUEs2OUEtcmtaMEw5WFdVRzRqTXZOQzNaZzc0QlB6NTNQQSIsInNraWQiOiJkaWQ6ZXhhbXBsZTphbGljZSNrZXkteDI1NTE5LTEiLCJhcHUiOiJaR2xrT21WNFlXMXdiR1U2WVd4cFkyVWphMlY1TFhneU5UVXhPUzB4IiwidHlwIjoiYXBwbGljYXRpb24vZGlkY29tbS1lbmNyeXB0ZWQranNvbiIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJhbGciOiJFQ0RILTFQVStBMjU2S1cifQ",
"recipients":[
{
"encrypted_key":"o0FJASHkQKhnFo_rTMHTI9qTm_m2mkJp-wv96mKyT5TP7QjBDuiQ0AMKaPI_RLLB7jpyE-Q80Mwos7CvwbMJDhIEBnk2qHVB",
"header":{
"kid":"did:example:bob#key-x25519-1"
}
},
{
"encrypted_key":"rYlafW0XkNd8kaXCqVbtGJ9GhwBC3lZ9AihHK4B6J6V2kT7vjbSYuIpr1IlAjvxYQOw08yqEJNIwrPpB0ouDzKqk98FVN7rK",
"header":{
"kid":"did:example:bob#key-x25519-2"
}
},
{
"encrypted_key":"aqfxMY2sV-njsVo-_9Ke9QbOf6hxhGrUVh_m-h_Aq530w3e_4IokChfKWG1tVJvXYv_AffY7vxj0k5aIfKZUxiNmBwC_QsNo",
"header":{
"kid":"did:example:bob#key-x25519-3"
}
}
],
"tag":"uYeo7IsZjN7AnvBjUZE5lNryNENbf6_zew_VC-d4b3U",
"iv":"o02OXDQ6_-sKz2PX_6oyJg"
}
In this example, the message is first signed with EdDSA digital signature and then encrypted with ECDH-1PU key wrapping algorithm using key with NIST defined P-521 elliptic curve and A256CBC-HS512 for content encryption of the message.
{
"ciphertext":"WCufCs2lMZfkxQ0JCK92lPtLFgwWk_FtRWOMj52bQISa94nEbIYqHDUohIbvLMgbSjRcJVusZO04UthDuOpSSTcV5GBi3O0cMrjyI_PZnTb1yikLXpXma1bT10D2r5TPtzRMxXF3nFsr9y0JKV1TsMtn70Df2fERx2bAGxcflmd-A2sMlSTT8b7QqPtn17Yb-pA8gr4i0Bqb2WfDzwnbfewbukpRmPA2hsEs9oLKypbniAafSpoiQjfb19oDfsYaWWXqsdjTYMflqH__DqSmW52M-SUp6or0xU0ujbHmOkRkcdh9PsR5YsPuIWAqYa2hfjz_KIrGTxvCos0DMiZ4Lh_lPIYQqBufSdFH5AGChoekFbQ1vcyIyYMFugzOHOgZ2TwEzv94GCgokBHQR4_qaU_f4Mva64KPwqOYdm5f4KX16afTJa-IV7ar7__2L-A-LyxmC5KIHeGOedV9kzZBLC7TuzRAuE3vY7pkhLB1jPE6XpTeKXldljaeOSEVcbFUQtsHOSPz9JXuhqZ1fdAx8qV7hUnSAd_YMMDR3S6SXtem8ak2m98WPvKIxhCbcto7W2qoNYMT7MPvvid-QzUvTdKtyovCvLzhyYJzMjZxmn9-EnGhZ5ITPL_xFfLyKxhSSUVz3kSwK9xuOj3KpJnrrD7xrp5FKzEaJVIHWrUW90V_9QVLjriThZ36fA3ipvs8ZJ8QSTnGAmuIQ6Z2u_r4KsjL_mGAgn47qyqRm-OSLEUE4_2qB0Q9Z7EBKakCH8VPt09hTMDR62aYZYwtmpNs9ISu0VPvFjh8UmKbFcQsVrz90-x-r-Q1fTX9JaIFcDy7aqKcI-ai3tVF_HDR60Jaiw",
"protected":"eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJObHJ3UHZ0SUluZWNpeUVrYTRzMi00czhPalRidEZFQVhmTC12Z2x5enFvIiwieSI6ImhiMnZkWE5zSzVCQ2U3LVhaQ0dfLTY0R21UT19rNUlNWFBaQ00xdGFUQmcifSwiYXB2Ijoiei1McXB2VlhEYl9zR1luM21qUUxwdXUyQ1FMZXdZdVpvVFdPSVhQSDNGTSIsInNraWQiOiJkaWQ6ZXhhbXBsZTphbGljZSNrZXktcDI1Ni0xIiwiYXB1IjoiWkdsa09tVjRZVzF3YkdVNllXeHBZMlVqYTJWNUxYQXlOVFl0TVEiLCJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLWVuY3J5cHRlZCtqc29uIiwiZW5jIjoiQTI1NkNCQy1IUzUxMiIsImFsZyI6IkVDREgtMVBVK0EyNTZLVyJ9",
"recipients":[
{
"encrypted_key":"ZIL6Leligq1Xps_229nlo1xB_tGxOEVoEEMF-XTOltI0QXjyUoq_pFQBCAnVdcWNH5bmaiuzCYOmZ9lkyXBkfHO90KkGgODG",
"header":{
"kid":"did:example:bob#key-p256-1"
}
},
{
"encrypted_key":"sOjs0A0typIRSshhQoiJPoM4o7YpR5LA8SSieHZzmMyIDdD8ww-4JyyQhqFYuvfS4Yt37VF4z7Nd0OjYVNRL-iqPnoJ3iCOr",
"header":{
"kid":"did:example:bob#key-p256-2"
}
}
],
"tag":"nIpa3EQ29hgCkA2cBPde2HpKXK4_bvmL2x7h39rtVEc",
"iv":"mLqi1bZLz7VwqtVVFsDiLg"
}
In this example, the message is first signed with EdDSA digital signature and then encrypted with ECDH-1PU key wrapping algorithm using key with X25519 elliptic curve and A256CBC-HS512 for content encryption of the message. After that the message is encrypted a second time with ECDH-ES key wrapping algorithm using key with X25519 elliptic curve and XC20P for content encryption of the message. The second anoncrypt is used to protect the sender ID.
{
"ciphertext":"lfYmR7CNas5hOePxWQEkUEwzSRds3t5GkMW4VUZKJWJ7H3y1X8a1RnUg3c0BCqdszzhZk8xE0vfQ67vJAWGdev8OWy7oGY_e1o4iAVj3mPNfnV5N7sjld6yUhrxqDsxtmVAp7LAipbJNhxqBoEXdb8hPbdPeUIov-5X0_cQHpHalSD6zMoyUPb0cCnw8bfmdN3aaVDrzsZRIkvhezZCkaQFMO75XKVEDyTzn8Eqwgpg_tzD_Hr00jHa9mTyTiDA_1ZzqleF-XSe5NEtFc7_BukgjPWMZAouPMWwIP0h-BPULxUzYcWKfC6hiU2ZuxWz8Fs8v9r6MCAaPOG37oA_yfWwE_FWl7x61sl6iZfDVQhOTkdlXNoZ0LiaC4ImXop2wSvKimkGqhysj1OefrUrpHmSx1qNz7vCWqW8Mo7fykXQCVYr6zXmcvWF5-KvXDu6DR3EFlgs6An9tWLv1flDrZWb-lS6RlL6Z8AqmLjP0Yb2r6mTopiulTTpXXpwe-Qs1_DHDGi0DfsZmcYhyra-F8YQ3tGIgy6wWCtyBh7Fq_zRy8RMvV3DkaLHYTekIle0YOoRdZRJBb3ycXHycIi7iT1ewOFlIGjsBg73Hkqa6O1weewS3uIxl4veO6cBOksfDRpC279X9tV1HDqROBolNBsWHQ2UpUD1Bat8UnfJMrwBcZkGQCjhlR9SSlZzEIqP3leRh5e2y2FGTm7wNRNwmgl6s6OUiKD-nbUnnSugGzolbavafHS80XrdfEuUyuPjnpQQQROapFfcjd7dSLd58g9OjOEqb1-Edk4KcW-yYU17_zfIzv1qykEH7F22Nq9HGbReXuao83ItUWgpBDZ-uf-_RbcpW2X1U5QGnI1SF4Trbhx74lnswEF_AlZ4SUh7frcMfKQLYobT1X_wIEY8pwN1AzWf482LJKKsxm0EcY73vf0n3uT_OS3EgBNCVYyF6_snm7MdOV-RM5ZZyQl64BsZ4aL4RVVCOa8bxYGPxvpOf9Ay-aQjwYQfyFxayRJiQWkywk8SRAdLLfSiveqvXAoIIi_XI98CRIaJ6DSKr-TuCDlz4yVP_8emS_S0S7F-Buh-P6nzjdJ04CAm95p6do_q8jk1IRHvubqrPKcpvk4U3p-6obJK9feJPffoe3-ddJvKJ5h8Et3xEKG7oId3NkbbFfYUnkEyC_wUeKtyrXK8uBz5HKhW1S27qsBAnKv5WTCyfrDsfX0eTaqdeJ3O9uR4niBc2sa2t89G5AEKWcOUnJcytAAAuhMZiz2zXXhmffPG5A7QSmZMAl75CP6ulN0KCBE0nTeuvNPueqpF4PV4CCcMfokz0hu5k5oo9FHfkQMVDBTiQUtEezIXiglqhu6VwcDgbbatAKUIYxnoisHKPg17zGMl5VMULVY5WBYPAUylKpWELnMc9BHUHNUxfSVlqdd847v__D1Go17MTsQujVGQQuM61Ay0-z1JwN0fki0M8t20U_sWX5jNMbdZCPBxy7rpZlztaF01j1NCaM3ZPh-_KLy8vQ584R5I5LlE5OejgyLQYMOMzSgUZZEAeTGV_S-kEnt36k-L8Kbyv_LWuiuTQzwLSwlmWOKLdDbmjEjA1JsEaKmorDKz0q7MFIoC-gKKJBjPTJ5PxJLJj4RHOxxDWhx00HjLLE3S1B6uAvKVUhN4ka_wWusVqffrRZm_e7Oz0hbCO8pT4tzlbFWTu0-O44kHkRjfubEi4PnaNzKbGMXTrDo7aY6sgiDB8KlJSsKrNeG0OLjBAYF_zmHlrqctFQidTD_YIDzcSfkCTrMoOYa07nXG6E1nArScOgkNuNkPVhCq_VD6w-pZ1mSUBwKVCnjNueTrB5RvFBydaoWcAAX3OtH8yFeDWGzlRYWJNKEKull_Vah8B7nwwnTPxyeUwnr2txlwDvLx9ASrl5CjwvLc9bL7jCa6SrWt3hPjvjDY4JdFxnCqyyXD11Mpt2kyA4TTBaBbzI5Kja6pKsCUw0QCTCfTBu7bKGTOJKai32c4WRXvpVgIowOzdyjtKD0LgnY2fRTpJWpcTMVAHPfSad0jc23iTwOKcJQ0n_ExfOxzW_PSvAYbakrRwdZdDefb_fLrILxgS7OA9KepGQOJnp0-X_o1bBkXsm_cvVhcprLViUxHR1uCTMXaUl24viekps45aODvfBj5OsG3GrEShqtLb7ukEHEJjLsIe1l-4kFtNp4RlPZlapYgNyMSjnGopw2D51khuOHdJ2yLWASgFJPIa4dan4KTcDhp7qmbijN8JR_s_p1DB4E1nFlQPuncA8lIiuGv2PKHKXQkkuHcKmPMYTjRlam5IBHXQPV_njHMAIV60XU8kxa5G7t-Iwl_6OeRIj_HXdf5mfdTNEYlwbQWHInkS4U32RD9Kf0u6SC1bpRZx6AbFK8xlIgUPhB_sP3kG_ZZIZhcJ1Oy6Q7pAzmKXZYWKMkDWZk7a-WsiA0Z8gOcd7PYA13GRIw0MT_GIRcFRfkp7821j2ArHHo6jagqMdEuCZHzHrfwD0XHzT4FP3-aTaHIqrKx0TiYRfn2k2Q",
"protected":"eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTUyMSIsIngiOiJBYmxoeVVENUxYNE9zWDhGRTVaODRBX09CYThiOHdhVUhXSFExbTBnczhuSFVERDdySDlJRWRZbzJUSzFQYU5ha05aSk54a1FBWC1aUkxWa1BoNnV4eTJNIiwieSI6IkFQTjh6c0xEZGJpVjN0LTloWTJFQzFVZWEzTm5tMzFtNWowRmNiUWM0Y2ZWQmFNdzVCQ2VpcU9QWkljZTVMNjI4bnVORkxKR2szSjh6SVBPYUlLU0xmaTEifSwiYXB2IjoiR09lbzc2eW02TkNnOVdXTUVZZlcwZVZEVDU2Njh6RWhsMnVBSVctRS1IRSIsInR5cCI6ImFwcGxpY2F0aW9uL2RpZGNvbW0tZW5jcnlwdGVkK2pzb24iLCJlbmMiOiJYQzIwUCIsImFsZyI6IkVDREgtRVMrQTI1NktXIn0",
"recipients":[
{
"encrypted_key":"iuVx5qAiRtijMfHnkF95_ByjHyiAmRqNTrExrEQK4p7HwW7sit1F0g",
"header":{
"kid":"did:example:bob#key-p521-1"
}
},
{
"encrypted_key":"6OWnv-tY1ZDUBt8uRNpmteoXTVDzRGz2UF04Y2eh2-bp2jiViU8VCw",
"header":{
"kid":"did:example:bob#key-p521-2"
}
}
],
"tag":"pEh6LS1GCTYQaWR-6vAe_Q",
"iv":"ZMHYqq1xV1X81bFzzEH_iAfBcL75fznZ"
}