§ 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:

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:

Before we provide more details, let’s explore what drives the design of DIDComm Messaging.

§ Specific Requirements

The DIDComm Messaging design attempts to be:

  1. 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)
  2. 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)
  3. 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)
  4. 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)
  5. 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)
  6. 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)
  7. Extensible (lets developers start simple without heavy learning or dependencies; customizes easily; facilitates higher-level protocols that inherit DIDComm Messaging’s guarantees)
  8. 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.

DIDComm envelopes

§ 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 Plaintext Message Icon

§ 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 Signed Message Icon

§ 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, or application/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):

DIDComm Encrypted Message Icon

§ 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:

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:

§ 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.

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.

§ 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:

  1. The type of service endpoint used by each party
  2. The key types used for encryption and/or signing
  3. The format of the encryption and/or signing envelopes
  4. The encoding of plaintext messages
  5. The protocol used to forward and route
  6. 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

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:

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:

Note: apuwill 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

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:

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

§ 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:

§ 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 on forward 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]"
  }
}

§ 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.

problem code structure

§ 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:

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:

§ 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:

§ 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:

  1. Implementations SHOULD consider implementing a circuit breaker design pattern to prevent this problem.
  2. Timeouts SHOULD be used judiciously.
  3. 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 the thid 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:

  1. The outermost message, decrypted by Bob’s external mediator, containing forwarding instructions to Bob’s cloud agent.
  2. The center message, decrypted by Bob’s cloud agent, containing an inner encrypted payload and instructions to forward it to Bob’s DID.
  3. 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:

§ 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.

§ WebSockets

Websockets are an efficient way to transmit multiple messages without the overhead of individual requests. This is useful in a high bandwidth situation.

§ 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:

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.

ordinary sequence

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 on forward 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
    ]
}

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:

re-wrapped sequence

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?

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

  1. Construct a plaintext message, M.
  2. If appropriate, sign M.
  3. 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.
  4. 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 plaintext forward 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.
  5. Transmit the fully wrapped version of N to the uri given in the associated serviceEndpoint 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.

  1. Receive ‘forward’ message.
  2. Retrieve service endpoint pre-configured by recipient (next attribute).
  3. 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:

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:

Example 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:

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,

These redirects may not be required in many cases, for example,

§ 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:

state machines]

§ 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"
}
Table of Contents