<abstract>This specification defines a protocol for end-to-end encryption in one-to-one chats, as well as group chats where each participant may have multiple clients per account.</abstract>
<p>It is a result of XMPP's federated nature that a message may pass more than just one server. Therefore it is in the users' interest to secure their communication from any intermediate host. End-to-end encryption is an efficient way to protect any data exchanged between sender and receiver against passive and active attackers such as servers and network nodes.</p>
<p>OMEMO is an end-to-end encryption protocol based on the Double Ratchet specified in section <linkurl="#protocol-double_ratchet">Double Ratchet</link>. It provides the following guarantees under the threat model described in the next section:</p>
<ul>
<li>Confidentiality: Nobody else except sender and receiver is able to read the content of a message.</li>
<li>Authentication: Every peer is able to authenticate the sender or receiver of a message, even if the details of the authentication process is out-of-scope for this specification.</li>
<li>Immutability: Every peer can ensure that a message was not changed by any intermediate node.</li>
<di><dt>Bundle</dt><dd>A collection of publicly accessible data used by the X3DH key exchange that can be used to build a session with a device, namely its public IdentityKey, a signed PreKey with corresponding signature, and a list of (single use) PreKeys.</dd></di>
<di><dt>rid</dt><dd>The device id of the intended recipient of the containing <key></dd></di>
<di><dt>sid</dt><dd>The device id of the sender of the containing OMEMO element</dd></di>
This protocol uses the Double Ratchet encryption scheme in conjunction with the X3DH key exchange. The following section provides detailed technical information about the protocol that should be sufficient to build an implementation of the OMEMO Double Ratchet. Readers who do not intend to build an OMEMO-compatible library can safely skip this section, relevant details are repeated where needed.
The X3DH key exchange is specified <linkurl="https://signal.org/docs/specifications/x3dh/">here</link> and placed under the public domain. OMEMO uses a modified version of this key exchange mechanism with the following parameters/settings:
<di><dt>signed PreKey rotation period</dt><dd>Signed PreKeys SHOULD be rotated periodically once a week to once a month. A faster or slower rotation period should not be required.</dd></di>
<di><dt>time to keep the private key of the old signed PreKey after rotating it</dt><dd>The private key of the old signed PreKey SHOULD be kept for another rotation period as defined above, to account for delayed messages using the old signed PreKey.</dd></di>
<di><dt>number of PreKeys to provide in the bundle</dt><dd>The bundle SHOULD always contain around 100 PreKeys.</dd></di>
<di><dt>minimum number of PreKeys to provide in the bundle</dt><dd>The bundle MUST always contain at least 25 PreKeys.</dd></di>
<di><dt>associated data</dt><dd>The associated data is created by concatenating the IdentityKeys of Alice and Bob: <tt>AD = Encode(IK_A) || Encode(IK_B)</tt></dd></di>
<di><dt>XEdDSA</dt><dd>OMEMO does not use XEdDSA as specified in the X3DH specification. Instead, OMEMO uses an Ed25519 key pair as the IdentityKey and converts this key pair into an X25519 key pair when required. This is mainly due to the fact that XEdDSA is not widely available (at the time of writing) and it is not trivial to write custom implementations of it. The impact of this change is minimal: the <tt>Sig(PK, M)</tt> and the <tt>DH(PK1, PK2)</tt> functions work slightly different as defined below.</dd></di>
<di><dt>Sig(PK, M)</dt><dd><tt>Sig(PK, M)</tt> represents the byte sequence that is an Ed25519 signature on the byte sequence <tt>M</tt> and verifies with public key <tt>PK</tt>, and which was created by signing <tt>M</tt> with <tt>PK</tt>'s corresponding private key.</dd></di>
<di><dt>DH(PK1, PK2)</dt><dd>The original definition of <tt>DH(PK1, PK2)</tt> applies with one exception: if either <tt>PK1</tt> or <tt>PK2</tt> corresponds to the IdentityKey, the respective key first has to be converted into its X25519 equivalent as per [TODO: find and link specification of the conversion algorithm]. This conversion is implemented by several crypto-libraries, for example <tt>libsodium</tt>, which exports the conversion as <tt>crypto_sign_ed25519_sk_to_curve25519</tt> and <tt>crypto_sign_ed25519_pk_to_curve25519</tt> for the private and public key, respectively (documented <linkurl="https://download.libsodium.org/doc/advanced/ed25519-curve25519">here</link>).</dd></di>
The key exchange is done just-in-time when sending the first message to a device. Thus, each key exchange message always also contains encrypted content as produced by the Double Ratchet encryption scheme below.
<p>NOTE: <tt>OMEMOMessage.proto</tt>, <tt>OMEMOAuthenticatedMessage.proto</tt> and <tt>OMEMOKeyExchange.proto</tt> refer to the protobuf structures as defined <linkurl="#protobuf-schema">here</link>.</p>
The Double Ratchet encryption scheme is specified <linkurl="https://signal.org/docs/specifications/doubleratchet/">here</link> and placed under the public domain. OMEMO uses this protocol with the following parameters/settings:
<di><dt>ratchet initialization</dt><dd>The Double Ratchet is initialized using the shared secret, ad and public keys as yielded by the X3DH key exchange, as explained in the Double Ratchet specification.</dd></di>
<di><dt>deletion policy for skipped message keys</dt><dd>Skipped message keys MUST be stored until MAX_SKIP message keys are stored. At that point, keys are discarded on a FIFO basis to make space for new message keys. Implementations SHOULD not keep skipped message keys around forever, but discard old keys on a different implementation-defined policy. It is RECOMMENDED to base this policy on deterministic events rather than time.</dd></di>
<di><dt>authentication tag truncation</dt><dd>Authentication tags are truncated to 16 bytes/128 bits.</dd></di>
<di><dt>CONCAT(ad, header)</dt><dd><tt>CONCAT(ad, header) = ad || OMEMOMessage.proto(header)</tt> NOTE: the <tt>OMEMOMessage.proto</tt> is initialized without the ciphertext, which is optional. NOTE: Implementations are not strictly required to return a parseable byte array here, as the unpacked/parsed data is required later in the protocol.</dd></di>
<di><dt>KDF_RK(rk, dh_out)</dt><dd>HKDF-SHA-256 using the rk as HKDF salt, dh_out as HKDF input material and "OMEMO Root Chain" as HKDF info.</dd></di>
<di><dt>KDF_CK(ck)</dt><dd>HMAC-SHA-256 using ck as the HMAC key, a single byte constant <tt>0x01</tt> as HMAC input to produce the next message key and a single byte constant <tt>0x02</tt> as HMAC input to produce the next chain key.</dd></di>
The encryption step uses authenticated encryption consisting of AES-256-CBC with HMAC-SHA-256.
<ol>
<li>Use HKDF-SHA-256 to generate 80 bytes of output from the message key by providing mk as HKDF input, 256 zero-bits as HKDF salt and "OMEMO Message Key Material" as HKDF info.</li>
<li>Divide the HKDF output into a 32-byte encryption key, a 32-byte authentication key and a 16 byte IV.</li>
<li>Encrypt the plaintext (which consists of a 32 bytes key and a 32 bytes HMAC as specified <linkurl="#protocol-message_encryption">here</link>) using AES-256-CBC with PKCS#7 padding, using the encryption key and IV derived in the previous step.</li>
<li>Split the associated data as returned by <tt>CONCAT</tt> into the original ad and the <tt>OMEMOMessage.proto</tt> structure.</li>
<li>Add the ciphertext to the <tt>OMEMOMessage.proto</tt> structure.</li>
<li>Serialize the ad and the <tt>OMEMOMessage.proto</tt> structure into a parseable byte array by concatenating ad and the serialized protobuf structure.</li>
<li>Calculate the HMAC-SHA-256 using the authentication key and the input material as derived in the steps above.</li>
<li>Put the <tt>OMEMOMessage.proto</tt> structure and the HMAC into a new <tt>OMEMOAuthenticatedMessage.proto</tt> structure.</li>
If encrypting this message required a key exchange, the X3DH key exchange header data is placed into a new <tt>OMEMOKeyExchange.proto</tt> structure together with the <tt>OMEMOAuthenticatedMessage.proto</tt> structure.
</p>
<p>
To account for lost and out-of-order messages during the key exchange, <tt>OMEMOKeyExchange.proto</tt> structures are sent until a response by the recipient confirms that the key exchange was successfully completed. To do so, the X3DH key exchange header data is stored and added on each subsequent message until a response is received. This looks roughly as follows:
</p>
<ol>
<li>The first content is encrypted for a new recipient. This results in an X3DH header and a <tt>OMEMOAuthenticatedMessage.proto</tt> structure. Both are packed into an <tt>OMEMOKeyExchange.proto</tt> structure. The X3DH header is stored for following messages.</li>
<li>A second message is encrypted for the same recipient. This results in only an <tt>OMEMOAuthenticatedMessage.proto</tt> structure, as a new key exchange is not required. Together with the X3DH header that was stored in the previous step, an <tt>OMEMOKeyExchange.proto</tt> structure is constructed and sent to the recipient.</li>
<li>Use HKDF-SHA-256 to generate 80 bytes of output from the key by providing the key as HKDF input, 256 zero-bits as HKDF salt and "OMEMO Payload" as HKDF info.</li>
<li>Divide the HKDF output into a 32-byte encryption key, a 32-byte authentication key and a 16 byte IV.</li>
<li>Encrypt the plaintext using AES-256-CBC with PKCS#7 padding, using the encryption key and IV derived in the previous step.</li>
<li>Calculate the HMAC-SHA-256 using the authentication key and the ciphertext from the previous steps.</li>
<li>Concatenate the key and the HMAC, encrypt them using the Double Ratchet as specified above, once for each intended recipient. This yields one OMEMOKeyExchange or OMEMOAuthenticatedMessage per recipient device.</li>
<li>Use HKDF-SHA-256 to generate 80 bytes of output from the key by providing the key as HKDF input, 256 zero-bits as HKDF salt and "OMEMO Payload" as HKDF info.</li>
<li>Divide the HKDF output into a 32-byte encryption key, a 32-byte authentication key and a 16 byte IV.</li>
<li>Verify the HMAC-SHA-256 using the authentication key derived in the previous step and the ciphertext.</li>
<li>Decrypt the ciphertext using AES-256-CBC with PKCS#7 padding, using the encryption key and IV derived in the previous steps.</li>
To participate in OMEMO-encrypted chats, clients need to set up an OMEMO library and generate a device id, which is a randomly generated integer between 1 and 2^31 - 1. The device id must be unique for the account.
<p>In order to determine whether a given contact has devices that support OMEMO, the devices node in PEP is consulted. Devices MUST subscribe to <tt>&nsdevices;</tt> via PEP, so that they are informed whenever their contacts add a new device. They MUST cache the most up-to-date version of the device list.</p>
<p>In order for other devices to be able to initiate a session with a given device, it first has to announce itself by adding its device id to the devices PEP node.</p>
<p>It is REQUIRED to set the access model of the <tt>&nsdevices;</tt> node to ‘open’ to give entities without presence subscription read access to the devices and allow them to establish an OMEMO session. Not having presence subscription is a common occurrence on the first few messages between two contacts and can also happen fairly frequently in group chats as not every participant had prior communication with every other participant.</p>
<p>The device element MAY contain an attribute called label, which is a user defined string describing the device that published that bundle. It is RECOMMENDED to keep the length of the label under 53 Unicode code points.</p>
<p>NOTE: as per <linkurl='https://xmpp.org/extensions/xep-0060.html#impl-singleton'><cite>XEP-0060</cite> §12.20</link>, it is RECOMMENDED for the publisher to specify an ItemID of "current" to ensure that the publication of a new item will overwrite the existing item.</p>
<p>This step presents the risk of introducing a race condition: Two devices might simultaneously try to announce themselves, unaware of the other's existence. The second device would overwrite the first one. To mitigate this, devices MUST check that their own device id is contained in the list whenever they receive a PEP update from their own account. If they have been removed, they MUST reannounce themselves.</p>
<p>Furthermore, a device MUST publish its IdentityKey, a signed PreKey, and a list of PreKeys. This tuple is called a bundle and is provided by OMEMO libraries. Bundles are maintained as multiple items in a PEP node called <tt>&nsbundles;</tt>. Each bundle MUST be stored in a seperate item. The item id MUST be set to the device id.</p>
<p>A bundle is an element called 'bundle' in the <tt>&ns;</tt> namespace. It has a child element called ‘spk’ that contains the public part of the signed PreKey as base64 encoded data, a child element called ‘spks’ that contains the signed PreKey signature as base64 encoded data and a child element called ‘ik’ that contains the public part of the IdentityKey as base64 encoded data. PreKeys are multiple elements called ‘pk’ that each contain the public part of one PreKey as base64 encoded data. PreKeys are wrapped in an element called ‘prekeys’ which is a child of the bundle element. The ‘spk’ and the ‘pk’s are tagged with an ‘id’-attribute which is a positive integer that uniquely identifies the keys. The ‘spk’ and the ‘pk’s are considered separate, which means that an ‘spk’ can have the same ‘id’ as a ‘pk’. These ids are used to save bandwidth during key exchanges, which refer to the keys using their id instead of their full public parts.</p>
<p>When publishing bundles a client MUST make sure that the <tt>&nsbundles;</tt> node is configured to store multiple items. This is not the default with &xep0163;. If the node doesn’t exist yet it can be configured on the fly by using publish-options as described in <linkurl="https://xmpp.org/extensions/xep-0060.html#publisher-publish-options"><cite>XEP-0060</cite> §7.1.5</link>. The value for 'pubsub#max_items' in publish_options MUST be set to 'max'. If the node did exist and was configured differently the bundle publication will fail. Clients MUST then reconfigure the node as described in <linkurl="https://xmpp.org/extensions/xep-0060.html#owner-configure"><cite>XEP-0060</cite> §8.2</link>.</p>
<li>MUST contain an <rpad/> affix element. This is used to prevent an attacker from gaining insights about the content of a message based on the length of the ciphertext.</li>
<li>MAY contain a <time/> affix element. This can be used to prevent the server from modifying the order in which messages from different sending devices have been sent.</li>
<li>SHOULD contain a <from/> affix element.</li>
<li>MUST contain a <to/> affix element whenever a message is sent via a group chat (MUC/MIX). This is used to prevent the server from silently converting a group message into a private message and vice versa.</li>
Clients MUST only consider the devices on the <tt>&nsdevices;</tt> node of each recipient (i.e. including their own devices node, but excluding itself).
An OMEMO encrypted message is specified to include an <encrypted> element in the 'urn:xmpp:omemo:1' namespace. It always contains two child nodes, the <header> and the &payload; element.
The <header> element has an attribute named 'sid' referencing the device id of the sending device and contains one or multiple <keys> elements, each with an attribute 'jid' of one of the recipients bare JIDs as well as one or multiple <key> elements.
A <key> element has an attribute named 'rid' referencing the device id of the recipient device, and an attribute named 'kex' which defaults to 'false' and indicates if the enclosed encrypted message includes a key exchange. The ciphertext that is the key and HMAC encrypted using the long-standing OMEMO session for that recipient device is encoded using base64 and placed as text content into the <key> element.
<p>When an OMEMO element is received, the client MUST check whether there is a <keys> element with a jid attribute matching its own bare jid and an inner <key> element with a rid attribute matching its own device id. If this is not the case the message was not encrypted for this particular device and a warning message SHOULD be displayed instead. If such an element exists, the client checks whether the element's contents are an OMEMOKeyExchange.</p>
<p>If this is the case, a new session is built from this received element. The client MUST then republish their bundle information, replacing the used PreKey, such that it won't be used again by a different client. If the client already has a session with the sender's device, it MUST replace this session with the newly built session. The client MUST eventually delete the private key belonging to the PreKey after use (this is subject to the <linkurl="#business-rules">Business rules</link>).</p>
<p>If the element's contents are a OMEMOAuthenticatedMessage, and the client has a session with the sender's device, it tries to decrypt the OMEMOAuthenticatedMessage using this session. If the decryption fails or there is no session with the sending device, a warning message SHOULD be displayed instead. Also refer to the section about recovering from broken sessions in the <linkurl="#business-rules">Business Rules</link>.</p>
After either the OMEMOKeyExchange or the OMEMOAuthenticatedMessage is decrypted, the content is decrypted as described in the section about <linkurl="#protocol-message_decryption">Message Decryption</link>.
<p>Note: OMEMO encrypted group chats are currently specified to work with &xep0045;. This XEP might be updated in the future to specify the usage of OMEMO in conjunction with &xep0369;.</p>
<p>A participant wanting to send a message to a group chat MUST first retrieve the members list and then fetch the device list for each member (via pubsub and to their real JIDs) and then subsequently fetch all bundles referenced by the device lists.</p>
<p>On join a participant MUST request the member list, the admin list and the owner list as described in <linkurl='https://xmpp.org/extensions/xep-0045.html#modifymember'><cite>XEP-0045</cite> §9.5</link>, <linkurl='https://xmpp.org/extensions/xep-0045.html#modifyadmin'><cite>XEP-0045</cite> §10.8</link>, and <linkurl='https://xmpp.org/extensions/xep-0045.html#modifyowner'><cite>XEP-0045</cite> §10.5</link> respectively. The real JIDs from those three lists MUST be combined as the recipients of OMEMO encrypted messages. This includes recipients who are currently offline. Once joined a participant MUST keep track of affiliation changes that occur in the room. This is both for removals (users getting banned or have their affiliation set to none) and users becoming members, admins or owners.</p>
<p>Sending a message to a group chat is similiar to sending a message in a 1:1 conversation. Instead of the <header> element having two <keys> elements (one for the recipient and one for other devices of the sender) it will contain multiple <keys> elements. One for each participant of the room; including, again, other devices of the sender.</p>
<examplecaption='Juliet sends a message to a group chat with Romeo and Mercutio'><![CDATA[
<p>Before publishing a freshly generated device id for the first time, a device MUST check whether that device id already exists, and if so, generate a new one.</p>
<p>Clients SHOULD NOT immediately fetch the bundle and build a session as soon as a new device is announced. Before the first message is exchanged, the contact does not know which PreKey has been used (or, in fact, that any PreKey was used at all). As they have not had a chance to remove the used PreKey from their bundle announcement, this could lead to collisions where both Alice and Bob pick the same PreKey to build a session with a specific device. As each PreKey SHOULD only be used once, the party that sends their initial OMEMOKeyExchange later loses this race condition. This means that they think they have a valid session with the contact, when in reality their messages MAY be ignored by the other end. By postponing building sessions, the chance of such issues occurring can be drastically reduced. It is RECOMMENDED to construct sessions only immediately before sending a message.</p>
<p>There are various reasons why decryption of an OMEMOKeyExchange or an OMEMOAuthenticatedMessage could fail. One reason is if the message was received twice and already decrypted once, in this case the client MUST ignore the decryption failure and not show any warnings/errors. In all other cases of decryption failure, clients SHOULD respond by forcibly doing a new key exchange and sending a new OMEMOKeyExchange with a potentially empty SCE payload. By building a new session with the original sender this way, the invalid session of the original sender will get overwritten with this newly created, valid session. This does NOT apply to the actual SCE content. If decrypting the SCE content fails, e.g. because the HMAC does not verify, this is not a reason to forcibly initiate a new key exchange.</p>
<p>If an OMEMOKeyExchange is received as part of a message catch-up mechanism (like &xep0313;) and used to establish a new session with the sender, the client SHOULD postpone deletion of the private key corresponding to the used PreKey until after the catch-up is completed. If this is done, the client MUST send an OMEMO encrypted message with empty SCE payload right after the key exchange is completed, to forward the ratchet and to move away from the possibly double-used PreKey. This practice can mitigate the previously mentioned race condition by preventing message loss.</p>
<p>When a client receives the first message for a given ratchet key with a counter of 53 or higher, it MUST send a heartbeat message. Heartbeat messages are normal OMEMO encrypted messages where the SCE payload does not include any elements. These heartbeat messages cause the ratchet to forward, thus consequent messages will have the counter restarted from 0.</p>
<p>When a client receives a message from a device id that is not on the device list, it SHOULD try to retrieve that user's devices node directly to ensure their local cached version of the devices list is up-to-date.</p>
<p>When the user of a client deactivates OMEMO for an account or globally, the client SHOULD delete the corresponding bundles and device ids from the PEP nodes. That way other clients should stop encrypting for that account.</p>
<section2topic='Server side requirements'anchor='server-side'>
<p>While OMEMO uses a Pubsub Service (&xep0060;) on the user’s account it has more requirments than those defined in &xep0163;. The requirements are:</p>
<ul>
<li>The pubsub service MUST persist node items.</li>
<li>The pubsub service MUST support publishing options as defined in <linkurl='https://xmpp.org/extensions/xep-0060.html#publisher-publish-options'><cite>XEP-0060</cite> §7.1.5</link>.</li>
<li>The pubsub service MUST support 'max' as a value for the 'pubsub#persist_items' node configuration.</li>
<p>Clients MUST NOT use a newly built session to transmit data without user intervention. If a client were to opportunistically start using sessions for sending without asking the user whether to trust a device first, an attacker could publish a fake device for this user, which would then receive copies of all messages sent by/to this user. A client MAY use such "not (yet) trusted" sessions for decryption of received messages, but in that case it SHOULD indicate the untrusted nature of such messages to the user.</p>
<p>When prompting the user for a trust decision regarding a key, the client SHOULD present the user with a fingerprint in the form of a hex-string, QR code, or other unique representation, such that it can be compared by the user. To ensure interoperability between clients and older versions of OMEMO, the fingerprint SHOULD be chosen to be the public part of the IdentityKey after converting it to X25519 (see the <linkurl="#protocol-key_exchange">notes on `DH(PK1, PK2)` in the X3DH protocol section</link> for info on that conversion). If interoperability and backward compatibility are not of concern, the fingerprint MUST still be chosen as a different combination of data that guarantees absence of a man-in-the-middle when verified. When displaying the fingerprint as a hex-string, one way to make it easier to compare the fingerprint is to split the hex-string into 8 substrings of 8 chars each, then coloring each 8-char group using &xep0392;. Lowercase letters are recommended when displaying the fingerprint as a hex-string.</p>
<p>While it is RECOMMENDED that clients postpone private key deletion until after message catch-up, the X3DH standard mandates that clients should not use duplicate-PreKey sessions for sending, so clients MAY delete such keys immediately for security reasons. For additional information on potential security impacts of this decision, refer to <note>Menezes, Alfred, and Berkant Ustaoglu. "On reusing ephemeral keys in Diffie-Hellman key agreement protocols." International Journal of Applied Cryptography 2, no. 2 (2010): 154-158.</note>.</p>
<p>Big thanks to Daniel Gultsch for mentoring me during the development of this protocol. Thanks to Thijs Alkemade and Cornelius Aschermann for talking through some of the finer points of the protocol with me. And lastly I would also like to thank Sam Whited, Holger Weiss, and Florian Schmaus for their input on the standard.</p>