End-to-end stanza security
WARNING: This document has not yet been accepted for consideration or approved in any official manner by the Jabber Software Foundation, and this document must not be referred to as a Jabber Enhancement Proposal (JEP). If this document is accepted as a JEP by the Jabber Council, it will be published at <http://www.jabber.org/jeps/> and announced on the <email@example.com> mailing list.
JIG: Standards JIG
Dependencies: XMPP Core, RFC 2440, RFC 2633
Superseded By: None
Short Name: secure
This Jabber Enhancement Proposal is copyright 1999 - 2004 by the Jabber Software Foundation (JSF) and is in full conformance with the JSF's Intellectual Property Rights Policy <http://jabber.org/jsf/ipr-policy.php>. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at <http://www.opencontent.org/openpub/>).
The preferred venue for discussion of this document is the Standards-JIG mailing list: <https://jabberstudio.org/mailman/listinfo/standards-jig>.
Version 0.1 (2004-03-15)
Initial version. (jk)
Stanza Security helps ensure confidentiality and integrity of transmitted XMPP stanzas between endpoints. It is intended for simple "one-shot" use, and it attempts to address all of the major problems in Current Jabber OpenPGP Usage . The most important feature this JEP brings forth is the ability to encrypt full stanzas. This is an important upgrade over JEP-0027, as it allows arbitrary XML to be encrypted instead of simply message text, thus bringing end-to-end security beyond IM.
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 .
This JEP addresses the following requirements:
4.1 Encoded payload format
Any stanza MAY be secured. To do this, a payload XML structure is created, which contains the full stanza to be secured, along with other information. The payload is then encoded into OpenPGP (RFC 2440 ) or S/MIME (RFC 2633 ) format. Before encoding, the XML to be encoded will be in the following form:
Example 1. Abstract format of payload<payload xmlns='http://jabber.org/protocol/secure'> [stanza] <id>[some id]</id> </payload>
The first child element of <payload> MUST be the actual stanza to be encoded, although other child elements may be in any order. Even if only signing is used, the full stanza must be present in the payload in order for the recipient to be able to properly verify the signature. In addition to the stanza, an <id> element MUST be present, which contains a unique ID for the payload. The CDATA of the <id> element MUST be constructed according to the following algorithm:
- concatenate the sender's full JID (user@host/resource) with the recipient's full JID
- concatenate the resulting string with a full ISO-8601 UTC timestamp including year, month, day, hours, minutes, seconds in the following format: yyyy-mm-dd-Thh:mm:ssZ (the timestamp must be UTC, no offsets are allowed)
- concatenate the resulting string with a psuedo-random 16-bit integer (in string form)
- hash the resulting string according to the SHA1 algorithm
- convert the hexidecimal SHA1 output to all lowercase
A <window> element SHOULD also be present, which contains a "Window of opportunity" value as child CDATA. This value is the number of seconds the recipient uses to account for clock drift while performing replay calculations.
A payload for a message stanza might look like this:
Example 2. Message payload with 10-minute window<payload xmlns='http://jabber.org/protocol/secure'> <message xmlns='jabber:client' firstname.lastname@example.org/orchard' type='chat'> <subject>Imploring</subject> <body>O Romeo, Romeo! Wherefore art thou Romeo?</body> </message> <id>e0ffe42b28561960c6b12b944a092794b9683a38</id> <window>600</window> </payload>
If the stanza to transmit is <presence>, then a <ttl> element SHOULD be present containing a "Time to live" value as child CDATA. This value is the number of seconds that the presence packet should be considered an accurate representation of the sender's state.
Example 3. Presence payload with 5-minute TTL<payload xmlns='http://jabber.org/protocol/secure'> <presence xmlns='jabber:client'> <show>away</show> <status>Up, up, and away!</status> </presence> <id>e0ffe42b28561960c6b12b944a092794b9683a38</id> <ttl>300</ttl> </payload>
Note: both the Window and TTL values MUST be in the range of 1 to 86400 (one day). If any such value is missing or out of range in a received payload, the value SHOULD be assumed to be 86400.
Next, the full <payload> element (including all XML tag names and angle brackets) MUST be converted into UTF-8 text and then encoded using either OpenPGP or S/MIME. The data MUST be signed and MAY be encrypted. The output MUST be armored US-ASCII with any headers removed. The resulting cipher text is then placed inside of a 'wrapper' stanza for transmission, as described in the next section.
4.2 Stanza format
Set the encoded payload as child CDATA of the <stanza> element, which is a child of the <secure> element. Set the 'type' attribute of the <secure> element to 'openpgp' (for OpenPGP) or 'smime' (for S/MIME), depending on the method used. This element is then placed inside of a wrapper stanza. The original stanza attributes MUST be replicated in the wrapper to ensure proper delivery.
Example 4. Abstract format of a secure stanza<[stanza-name] to='recipient' type='[value if provided]' id='[value if provided]' xml:lang='[value if provided]'> <secure xmlns='http://jabber.org/protocol/secure' type='[method type]'> <stanza> [encoded data] </stanza> </secure> </[stanza-name]>
For <message> and <presence> stanzas, other elements MAY be included as children of the top-level stanza element. While such data is not protected, this can be useful for allowing insecure clients to still process the packet. For example, a message might contain body text, "This message is encrypted". Presence stanzas might contain the complete original presence elements in addition to the secure data, useful if the packet is only signed.
Below are some examples of what secure stanzas might look like. The first stanza in each example is the original insecure version, the second is the secured form.
Example 5. message<message email@example.com'> <body>hello, world</body> </message> <message firstname.lastname@example.org'> <body>This message is encrypted.</body> <secure xmlns='http://jabber.org/protocol/secure' type='smime'> <stanza> hQIOAzvrdC3HvR3xEAgAhvN7grKZzC70OBHD/to2F0kz2Z3zQ3GDtzRK7hBGS0Zg oRJS3dEPgwViluVIWJWB1kHmrrjHPi5P0Nf1kRp5oFB2ZztOL/Ag04dMUuaSg1ga GCssRgp6WB1foDTgkXWSdj58fPhJV48uiXPzjUI7HdV8yScfZfMdG7m5GLH5KlEk +O5KMEBr6mSYsi7TIu30ezV/ZqmCLB3eF0+Ha+cx93Eczls4HVfSbIBR33on/mRE okZOhq0B4F3CixhtpNoQSwFKm2oZkdfdZCl4/GWQrcnv1r4jyvzXDw11cvALyaDP HhOxSjod0d6zdzUTOnjQCWrYE3IcfhcBxno47ha5lwf/W8g3DTkJhKNZLQoh5rY0 y2WKdecpAW2tmztTLxupHHnfvW6U2OYv1TRD+hCe9/oEb/JgyhIwfaFeDX0VlJR3 3M+F2oiOQHVEzLOFEMKouOCzsxzy7qZGaggh5lnjxj/OgxHYgioAwcw9db6U6XvC bA1anPXWpjl532xaW2d1TDot/0m1/aq0/dT5glNe+E+tS0NvEgsMr4t6qyLkkwmV ttUCXi/gTaD16FbFExtzt1lIPYJ2YmiiCX5WAG1FO0JV8BelMrbCD3EP3X4lbGTS ea30eB1dd07x9+KwB6XMNhJLBhNJNyLYqisRWy0fu2mfxtoKpB8xLBV33RWWvEb4 DtJ7AZQ8s62nK0TIrHVml+IV3b+kyfMQs4LWKhORy5OPClxecqIn0QskezFE9+zT yCC/3kZJBn9lSYrpLUTBAEf8QwlttHRncXtAEjgJC+b1rmRK8drViBSRvc0ITOZO SiqUJBaVOFnyEQYY3oIoNKifMUnUKHbWBSsjEdCy =Sd66 </stanza> </secure> </message>
Example 6. presence<presence> <show>away</show> <status>Up, up, and away!</status> </presence> <presence> <show>away</show> <status>Up, up, and away!</status> <secure xmlns='http://jabber.org/protocol/secure' type='openpgp'> <stanza> owGbwMvMwCT4+XHsM8mvJfMYT4clMTiEWSnYFBSlFqfmJafacSko2BRn5JfbJZYn Vtrog5lgsZLEktJiu9ACHYVSIE7MS1EAqVAEKoHIcNnoIwzpsGdmBRsMs0mQSXkD w3z/y6m7G7/w72Msj0udIN7w5RabM1A0jeea/KpnVnwv19d+5+TIuTLrq3k3AA== =dL9W </stanza> </secure> </presence>
Example 7. iq<iq email@example.com/Home' type='get' id='iq_1'> <query xmlns='jabber:iq:version'/> </iq> <iq firstname.lastname@example.org/Home' type='get' id='iq_1'> <secure xmlns='http://jabber.org/protocol/secure' type='openpgp'> <stanza> hQIOAzvrdC3HvR3xEAf/eEghJxnNU3N33PkkBBNCNfH1ckP5XSG0ZtgF15lh+djk NXfCdH0FABzgLZOjiP/+cbfnIGLUbVgexZ5g45f+u5nwE98m/E1JxHfLSA7bRr7B Sc7tDKgkPtjTpbW1OwDJAszqb4Km8bd7apHCrWGaYrEPWBVXqY01Os7vBE4BMFYb cpqnNuDY2YouA5YhmG3mZq465Bl9lTfKmh9msraT7wiAqyWZOk9elWK558ZG+opZ gUWqepaHuw1YVNX5AD99q4MMKJjZ25qSXOA0RaFLhfkAjvFCy45Dubg+x+4COuFj ia4Hhe+VPiXZBW1wf4i1hNLSSn/7wz3ZExlUtlW4Egf/YttRyMjHXQkU5uI18zfs WqPkzMpX6jGd+nhVSYaQWVixYeOLXHdpk5hdOSxKQ75fDt+qFGUSVZ+Eoq4eToz9 rXV+Kz4Yke4gjRLNTsyJWq87rl+RN0T4jSCDE6uwFyU3G0YYcgAJFNwPp0WZPTJU hzWN+QyqNfQHrBX1tMwMWfcUnl6x6uHW2I1BqhdDZn3Rg25MeFri6YusyGj7U1GE FK3XHTNxTLSbDrHxeYwXpiPZy3yFeKUc3/daeAL5l/IK2bRqUtvAA0iJTjNmaPFF 7EQOcRyWwNUjb2pRECYSvRlEqEkJHTFxvTkfY/psXnPu+cn9GJMD8St0tI4qxzEX HNKYAe2dxNeY578Z1CvDr95JILoX4cSvc1DfT9LNL+D54ahb1ClIIz5hEi6Blf4M rMdoKzU12sK+VBNg71PDU4ukSKgd12FpokZ0fthYwaHdGZJcSd3ix5sN1bAjgVpx EtMBsvfzCK6pSHqCKAH4R7dH7okmaZOEb1Lxo7evxR/97cJpnm7637wtkzB6BiJu dWXKifb7RW4ynDo= =O27w </stanza> </secure> </iq>
4.3 Handling received secure stanzas
Upon receiving a stanza, first check to see if it is secure. For <iq> this should be immediately obvious, as the first namespaced child element will be the <secure> element. For <message> and <presence> there might be many child elements, and so the <secure> element must be located.
Now, based on the 'type' specified in the <secure> element, decode the child CDATA of <stanza>. If this data is being passed to an external application or library, it might be necessary to reconstruct the appropriate headers first. If there is an error decoding the information, the recipient SHOULD report an error:
Example 8. Error decoding a message<message email@example.com' firstname.lastname@example.org' type='error'> <error type='cancel'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'> Cannot decode secure stanza </text> </error> </message>
Note that such errors MUST NOT be sent in response to any stanza of type 'error' or of an iq stanza of type 'result', for compliance with XMPP-Core.
The result of the decoding operation should yield a payload structure in UTF-8 format. It should be parsed using a separate parser instance than the one used for the XMPP session, as an error decoding the payload should not indicate an error in the entire XMPP session. Ensure that the stanza and id are present in the structure. If there is an error parsing the payload or retrieving its content, inform the sender:
Example 9. Error parsing a message<message email@example.com' firstname.lastname@example.org' type='error'> <error type='cancel'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'> Cannot parse payload </text> </error> </message>
Next, ensure the decoded stanza is kosher:
- The the decoded stanza MUST have the same element name and namespace as the wrapper stanza.
- The 'to' address MUST be valid for this endpoint according to the following guideline:
- <message> - bare-JID match
- <presence> - bare-JID match, or no JID specified (lack of a JID indicates a presence broadcast)
- <iq> - exact JID match
- The 'from' address MUST be a bare-JID match of the owner of the encoded signature.
If any of these checks fail, the packet MUST be dropped and not used.
Next, ensure this stanza is not a potentially 'replayed' stanza from earlier. The following terms are defined:
- expirationBase - If the stanza was received during this session, then let expirationBase be equal to the signature timestamp, else let it be equal to the current XMPP session start time.
- lowerBound - If the wrapper stanza is an offline message (ie, using Delayed Delivery  or Flexible Offline Message Retrieval ), then let lowerBound be equal to the end time of last XMPP session in which offline message history retrieval was completed, else let it be equal to (current time - window). If using Flexible Offline Message Retrieval, this completion time is once all offline messages have been received or purged. Otherwise, this time is (current XMPP session start + 10 minutes), or session end, whichever is sooner.
- upperBound - If the wrapper stanza is an offline message, then let upperBound be equal to the time of offline message history retrieval completion, else let it be equal to (current time - window).
- The signature timestamp MUST be later than lowerBound (if known) and earlier than upperBound.
- The payload id MUST be unique among any other known ids from this sender, in which (expirationBase + [window * 2]) for such ids does not exceed the current time (but only if offline message history retrieval is not complete for the current XMPP session). In order for this check to be performed, the receiver MUST record the necessary information from all payloads (sender, id, signature timestamp, window) so that they may be referred to later. This information SHOULD be retained between XMPP sessions.
If any of these checks fail, the stanza might have been replayed. Therefore, the stanza SHOULD NOT be processed. However, the receiver MAY ignore this indication if the stanza is known to be legitimately replayable in the given scenario (such as groupchat history playback or normal presence usage).
If these checks prove successful, then the stanza within the payload should be treated as a successfully received stanza, with the additional knowledge that it was transmitted securely. The insecure data within the wrapper stanza SHOULD NOT be used.
Signing of presence is useful not only for protecting the integrity of such status information, but also for proving one's availability at a given moment. At the same time, broadcasted presence is often replayed legitimately by the server, and so normal replay protection processing is not desirable. In this instance, an implementation may wish to skip the replay attack checks and utilize the "Time to live" value instead. Presence data should only be considered accurate if the TTL has not yet expired based on the signature timestamp. A sender should re-sign and transmit a new secure presence packet before the previous TTL expires. The TTL is meaningless for unavailable presence.
5.2 Unavailable presence
Proving one's availability can sometimes be impossible, due to network conditions or an attacker preventing packet transmission. Worse yet, the lack of available presence indicates one's unavailability, despite that there may be no signature to prove this. Therefore, all unavailable presence stanzas should be considered to be a true indication of a contact's unavailability, whether or not they are signed. Of course, signed unavailable presence is still useful, for determining the integrity of possible packet content (like a logoff string).
In order to be able to match public keys to their respective Jabber IDs, the JID is to be specified in the key itself. For X.509 certificates, the same rules detailed in XMPP-Core are to be followed. For OpenPGP, the JID should be stored in a user-id field, either using the 'xmpp' prefix: "Alice <xmpp:email@example.com>" or not: "Alice <firstname.lastname@example.org>". Implementations should prefer an OpenPGP key that has an 'xmpp' prefix over one that does not.
This JEP requires no interaction with the Internet Assigned Numbers Authority (IANA) .
9.1 Protocol Namespaces
The Jabber Registrar  shall register the 'http://jabber.org/protocol/secure' namespace as a result of this JEP.
<?xml version='1.0' encoding='UTF-8'?> <xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' targetNamespace='http://jabber.org/protocol/secure' xmlns='http://jabber.org/protocol/secure' elementFormDefault='qualified'> <xs:element name='secure'> <xs:complexType> <xs:element ref='stanza' maxOccurs='1'/> <xs:attribute name='type' use='required'/> <xs:simpleType> <xs:restriction base='xs:NCName'> <xs:enumeration value='openpgp'/> <xs:enumeration value='smime'/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> </xs:element> <xs:element name='payload'> <xs:complexType> <xs:any namespace='jabber:client' maxOccurs='1'> <xs:element ref='id' maxOccurs='1'/> <xs:element ref='window' minOccurs='0' maxOccurs='1'/> <xs:element ref='ttl' minOccurs='0' maxOccurs='1'/> </xs:complexType> </xs:element> <xs:element name='stanza' type='xs:string'/> <xs:element name='id' type='xs:string'/> <xs:element name='window' type='xs:string'/> <xs:element name='ttl' type='xs:string'/> </xs:schema>
1. JEP-0027: Current Jabber OpenPGP Usage <http://www.jabber.org/jeps/jep-0027.html>.
2. RFC 2119: Key words for use in RFCs to Indicate Requirement Levels <http://www.ietf.org/rfc/rfc2119.txt>.
3. RFC 2440: OpenPGP Message Format <http://www.ietf.org/rfc/rfc2440.txt>.
4. RFC 2633: S/MIME Version 3 Message Specification <http://www.ietf.org/rfc/rfc2633.txt>.
5. JEP-0091: Delayed Delivery <http://www.jabber.org/jeps/jep-0091.html>.
6. JEP-0013: Flexible Offline Message Retrieval <http://www.jabber.org/jeps/jep-0013.html>.
7. The Internet Assigned Numbers Authority (IANA) is the central coordinator for the assignment of unique parameter values for Internet protocols. For further information, see <http://www.iana.org/>.
8. The Jabber Registrar maintains a list of reserved Jabber protocol namespaces as well as registries of parameters used in the context of protocols approved by the Jabber Software Foundation. For further information, see <http://www.jabber.org/registrar/>.