mirror of https://github.com/moparisthebest/xeps
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1029 lines
41 KiB
1029 lines
41 KiB
<?xml version='1.0' encoding='UTF-8'?> |
|
<!DOCTYPE xep SYSTEM 'xep.dtd' [ |
|
<!ENTITY signcrypt "<signcrypt/>"> |
|
<!ENTITY sign "<sign/>"> |
|
<!ENTITY crypt "<crypt/>"> |
|
<!ENTITY openpgp "<openpgp/>"> |
|
<!ENTITY payload "<payload/>"> |
|
<!ENTITY % ents SYSTEM 'xep.ent'> |
|
%ents; |
|
]> |
|
<?xml-stylesheet type='text/xsl' href='xep.xsl'?> |
|
<xep> |
|
<header> |
|
<title>OpenPGP for XMPP</title> |
|
<abstract>Specifies end-to-end encryption and authentication of data with the help of |
|
OpenPGP, announcement, discovery and retrieval of public keys and a |
|
mechanism to synchronize secret keys over multiple |
|
devices.</abstract> |
|
&LEGALNOTICE; |
|
<number>0373</number> |
|
<status>Experimental</status> |
|
<type>Standards Track</type> |
|
<sig>Standards</sig> |
|
<approver>Council</approver> |
|
<dependencies> |
|
<spec>XMPP Core</spec> |
|
<spec>XEP-0030</spec> |
|
<spec>XEP-0082</spec> |
|
<spec>XEP-0163</spec> |
|
<spec>XEP-0223</spec> |
|
<spec>XEP-0334</spec> |
|
</dependencies> |
|
<supersedes/> |
|
<supersededby/> |
|
<shortname>ox</shortname> |
|
&flow; |
|
<author> |
|
<firstname>Dominik</firstname> |
|
<surname>Schürmann</surname> |
|
<email>dominik@dominikschuermann.de</email> |
|
<jid>dominik@dominikschuermann.de</jid> |
|
</author> |
|
<author> |
|
<firstname>Vincent</firstname> |
|
<surname>Breitmoser</surname> |
|
<email>look@my.amazin.horse</email> |
|
<jid>valodim@stratum0.org</jid> |
|
</author> |
|
<revision> |
|
<version>0.7.0</version> |
|
<date>2021-05-04</date> |
|
<initials>ps</initials> |
|
<remark> |
|
<p>Recommend PubSub access model 'open' for public key data node and metadata node.</p> |
|
</remark> |
|
</revision> |
|
<revision> |
|
<version>0.6.0</version> |
|
<date>2020-11-22</date> |
|
<initials>fs</initials> |
|
<remark> |
|
<p>Fix 'to'-attribute requirements: All content elements which are signed using OpenPGP need |
|
that attribute to prevent Surreptitious Forward Attacks. The &crypt; element does not require |
|
one, as the intented recipient is established by the encryption itself. The XEP had the |
|
requirements for &sign; and &crypt; mixed up.</p> |
|
</remark> |
|
</revision> |
|
<revision> |
|
<version>0.5.0</version> |
|
<date>2020-06-19</date> |
|
<initials>fs</initials> |
|
<remark> |
|
<ul> |
|
<li>Use RFC 4880 terminology: it is "primary key", not "master key".</li> |
|
<li>Clarify encryption of secret key material.</li> |
|
<li>Move the information from the 'date' attribute into the item ID.</li> |
|
</ul> |
|
</remark> |
|
</revision> |
|
<revision> |
|
<version>0.4.0</version> |
|
<date>2018-07-30</date> |
|
<initials>ps</initials> |
|
<remark>Fix node name in examples</remark> |
|
</revision> |
|
<revision> |
|
<version>0.3.3</version> |
|
<date>2018-07-30</date> |
|
<initials>ps</initials> |
|
<remark>Improve note about OpenGPG fingerprint; editorial fixes</remark> |
|
</revision> |
|
<revision> |
|
<version>0.3.2</version> |
|
<date>2018-07-05</date> |
|
<initials>ps</initials> |
|
<remark>Add example and small editorial fixes</remark> |
|
</revision> |
|
<revision> |
|
<version>0.3.1</version> |
|
<date>2018-05-21</date> |
|
<initials>ps</initials> |
|
<remark>Fix slightly incorrect reference to RFC 4880</remark> |
|
</revision> |
|
<revision> |
|
<version>0.3.0</version> |
|
<date>2018-04-16</date> |
|
<initials>fs</initials> |
|
<remark> |
|
Split public keys into metadata and data nodes. |
|
</remark> |
|
</revision> |
|
<revision> |
|
<version>0.2.1</version> |
|
<date>2017-11-13</date> |
|
<initials>fs</initials> |
|
<remark> |
|
<ul> |
|
<li>Recommend setting the PubSub configuration field 'send_last_published_item' to 'on_sub'.</li> |
|
<li>Only recommend persistent PubSub nodes.</li> |
|
</ul> |
|
</remark> |
|
</revision> |
|
<revision> |
|
<version>0.2</version> |
|
<date>2017-09-11</date> |
|
<initials>XEP Editor (jwi)</initials> |
|
<remark>Defer due to lack of activity.</remark> |
|
</revision> |
|
<revision> |
|
<version>0.1.3</version> |
|
<date>2016-07-15</date> |
|
<initials>fs (XEP Editor: ssw)</initials> |
|
<remark><p>Update acknowledgements.</p></remark> |
|
</revision> |
|
<revision> |
|
<version>0.1.2</version> |
|
<date>2016-07-11</date> |
|
<initials>bjc (XEP Editor: ssw)</initials> |
|
<remark><p>Minior editorial fixes.</p></remark> |
|
</revision> |
|
<revision> |
|
<version>0.1.1</version> |
|
<date>2016-06-04</date> |
|
<initials>fs</initials> |
|
<remark><p>Minior editorial fixes.</p></remark> |
|
</revision> |
|
<revision> |
|
<version>0.1</version> |
|
<date>2016-05-10</date> |
|
<initials>XEP Editor (ssw)</initials> |
|
<remark><p>Initial published version approved by the XMPP Council.</p></remark> |
|
</revision> |
|
<revision> |
|
<version>0.0.1</version> |
|
<date>2016-03-25</date> |
|
<initials>fs</initials> |
|
<remark><p>First draft.</p></remark> |
|
</revision> |
|
</header> |
|
|
|
<section1 topic='Introduction' anchor='intro'> |
|
|
|
<p>This XMPP extension protocol specifies the foundations of |
|
end-to-end encryption and authentication, based on digital |
|
signatures, of data with the help of OpenPGP. Additional XEPs will |
|
use this extension protocol as building block when specifying their |
|
own OpenPGP profile suiting their use case. One such profile is the |
|
Instant Messaging Profile specified in &xep0374;.</p> |
|
|
|
<p>XMPP provides the mechanisms to solve a lot of issues that come |
|
with modern day OpenPGP usage. For example, based on &xep0163; this |
|
specification describes a standardized way to discover OpenPGP |
|
public keys of other entities. But unlike the OpenPGP keyservers, |
|
this process establishes a strong relation between the key and the |
|
key's owning entity (usually a human user). A similar mechanism |
|
described herein allows to synchronize the secret key(s) across |
|
multiple devices.</p> |
|
|
|
<p>OpenPGP in return allows for end-to-end encrypted data to be |
|
exchanged between one, two or even multiple entities |
|
(multi-end-to-multi-end encryption). Therefore this XEP can be used |
|
for example to implement end-to-end encrypted &xep0045;.</p> |
|
|
|
</section1> |
|
|
|
<section1 topic='Glossary' anchor='glossary'> |
|
|
|
<dl> |
|
<di><dt>OpenPGP element</dt><dd>An XMPP extension element: &openpgp; qualified by the 'urn:xmpp:openpgp:0' namespace</dd></di> |
|
<di><dt>OpenPGP content element</dt><dd>An element embedded via OpenPGP in a &openpgp; element. Either one of &signcrypt;, &sign; or &crypt;, qualified by the 'urn:xmpp:openpgp:0' namespace.</dd></di> |
|
<di><dt>PEP</dt><dd>&xep0163;</dd></di> |
|
<di><dt>Public-Key metadata node ("metadata node")</dt><dd>A PEP node containing metadata of the entity's public OpenPGP key.</dd></di> |
|
<di><dt>Public-Key data node ("data node")</dt><dd>A PEP node containing an entity's public OpenPGP key.</dd></di> |
|
<di><dt>Secret-Key node</dt><dd>A PEP node containing an entity's encrypted secret OpenPGP key.</dd></di> |
|
<di><dt>OpenPGP v4 Fingerprint String</dt><dd>A String representing the OpenPGP v4 fingerprint |
|
of a key. If the key consists of a primary key and subkeys, this is the fingerprint of the |
|
primary key.</dd></di> |
|
</dl> |
|
|
|
</section1> |
|
|
|
<section1 topic='OpenPGP Encrypted and Signed Data' anchor='signcrypt'> |
|
|
|
<section2 topic='Exchanging OpenPGP Encrypted and Signed Data' anchor='exchange'> |
|
|
|
<p>The &openpgp; extension element qualified by the |
|
'urn:xmpp:openpgp:0' namespace is used in order to exchange |
|
encrypted and signed data.</p> |
|
|
|
<example caption='The &openpgp; extension within a message.'><![CDATA[ |
|
<message to='juliet@example.org'> |
|
<openpgp xmlns='urn:xmpp:openpgp:0'> |
|
BASE64_OPENPGP_MESSAGE |
|
</openpgp> |
|
</message>]]></example> |
|
|
|
<p>The text content of &openpgp; ("BASE64_OPENPGP_MESSAGE") is a |
|
Base64 encoded (&rfc4648; <link |
|
url='https://tools.ietf.org/html/rfc4648#section-4'>§ 4</link>) |
|
OpenPGP message as specified in &rfc4880; which contains an |
|
encrypted and/or signed UTF-8 (&rfc3629;) encoded string. This |
|
string MUST correspond to exactly one OpenPGP content element, |
|
that is, it represents either a &signcrypt;, a &sign; or a &crypt; |
|
extension element qualified by the 'urn:xmpp:openpgp:0' |
|
namespace. Note that OpenPGP's ASCII Armor is not used, instead |
|
the XMPP client MUST encode the raw bytes of the OpenPGP message using |
|
Base64.</p> |
|
|
|
<p>In case of a &signcrypt; element, the OpenPGP message embedded |
|
in the &openpgp; element MUST be encrypted and signed, and SHOULD |
|
also be encrypted to self. In case of a &sign; element, the |
|
OpenPGP message MUST be signed and MUST NOT be encrypted. In case |
|
of &crypt; the OpenPGP message MUST NOT be signed, but MUST be |
|
encrypted.</p> |
|
|
|
<example caption='The &signcrypt; extension element.'><![CDATA[ |
|
<signcrypt xmlns='urn:xmpp:openpgp:0'> |
|
<to jid='juliet@example.org'/> |
|
<time stamp='2014-07-10T17:06:00+02:00'/> |
|
<rpad> |
|
f0rm1l4n4-mT8y33j!Y%fRSrcd^ZE4Q7VDt1L%WEgR!kv |
|
</rpad> |
|
<payload> |
|
<body xmlns='jabber:client'> |
|
This is a secret message. |
|
</body> |
|
</payload> |
|
</signcrypt>]]></example> |
|
|
|
<p>OpenPGP content elements MUST possess exactly one 'time' |
|
element as direct child elements. The &signcrypt; and &sign; |
|
content elements MUST contain at least one 'to' element(s), which |
|
MUST have a 'jid' attribute containing the intended recipient's |
|
XMPP address of the signed and/or encrypted data to prevent |
|
Surreptitious Forward Attacks<note>Jee Hea An, Yevgeniy Dodis, and |
|
Tal Rabin. 2002. On the Security of Joint Signature and |
|
Encryption. In Proceedings of the International Conference on the |
|
Theory and Applications of Cryptographic Techniques: Advances in |
|
Cryptology (EUROCRYPT '02), Lars R. Knudsen |
|
(Ed.). Springer-Verlag, London, UK, UK, 83-107. <<link |
|
url='https://www.iacr.org/archive/eurocrypt2002/23320080/adr.pdf'>https://www.iacr.org/archive/eurocrypt2002/23320080/adr.pdf</link>></note>. |
|
The XMPP address found in the 'to' element's 'jid' attribute |
|
SHOULD be without Resourcepart (i.e., a bare JID). A &crypt; content |
|
element may not carry a 'to' attribute. The 'time' element MUST |
|
have a 'stamp' attribute which contains the timestamp when the |
|
OpenPGP content element was signed and/or encrypted in the |
|
DateTime format as specified in &xep0082; § 3.2. The &signcrypt; |
|
and &crypt; elements SHOULD furthermore contain a 'rpad' element |
|
which text content is a random-length random-content padding.</p> |
|
|
|
<table caption='OpenPGP Content Element Properties'> |
|
<tr> |
|
<th>Content Element</th> |
|
<th>'to' Element</th> |
|
<th>'time' Element</th> |
|
<th><rpad/> Element</th> |
|
<th><payload/> Element</th> |
|
</tr> |
|
<tr> |
|
<td>&signcrypt;</td> |
|
<td>MUST have at least one</td> |
|
<td>MUST have exactly one</td> |
|
<td>SHOULD have exactly one</td> |
|
<td>MUST have exactly one</td> |
|
</tr> |
|
<tr> |
|
<td>&sign;</td> |
|
<td>MUST have at least one</td> |
|
<td>MUST have exactly one</td> |
|
<td>OPTIONAL</td> |
|
<td>MUST have exactly one</td> |
|
</tr> |
|
<tr> |
|
<td>&crypt;</td> |
|
<td>OPTIONAL</td> |
|
<td>MUST have exactly one</td> |
|
<td>SHOULD have exactly one</td> |
|
<td>MUST have exactly one</td> |
|
</tr> |
|
</table> |
|
|
|
<p>OpenPGP content elements MUST possess exactly one &payload; |
|
element. The child elements of &payload; can be seen as OpenPGP |
|
secured Stanza extension elements which are encrypted and/or |
|
signed. After the &openpgp; element and the including &signcrypt;, |
|
&sign; or &crypt; element was verified, they are processed |
|
according to the specification of the relevant OpenPGP for XMPP |
|
profile (see for example &xep0374;).</p> |
|
|
|
</section2> |
|
|
|
<section2 topic='Verification of &openpgp; Content' anchor='openpgp-verification'> |
|
|
|
<p>Recipients MUST verify that the signature is valid, that the |
|
signature's key corresponds to the sender's key, and that the |
|
sender's key has a User ID containing the sender's XMPP |
|
address in the form "xmpp:juliet@example.org" (for details see |
|
<link url='#openpgp-user-ids'>"OpenPGP User IDs"</link>). Thus, |
|
the recipient may |
|
need to retrieve the key from the Personal Eventing Protocol node |
|
as described above. At least one of the XMPP addresses found in |
|
the 'to' elements contained in OpenPGP content element MUST |
|
correspond to the outer 'to' of the XMPP &MESSAGE;. Furthermore, |
|
recipients are RECOMMENDED to verify the 'time' element for |
|
plausibility or to display it to a user for verification.</p> |
|
|
|
</section2> |
|
|
|
</section1> |
|
|
|
<section1 topic='Announcing and Discovering Public Keys via PEP' anchor='announcing-discover-pubkey'> |
|
|
|
<p>Parties interested in exchanging encrypted data between each |
|
other via OpenPGP need to know the public key(s) of the |
|
recipients. The following section specifies a mechanism to announce |
|
and discover public keys.</p> |
|
|
|
<p>Two PEP node types are invovled: A "medatata node" is used to store meta information about |
|
OpenPGP keys used by an entity while the actual public keys are stored in "data nodes".</p> |
|
|
|
<section2 topic='The OpenPGP Public-Key Data Node' anchor='announcing-pubkey'> |
|
|
|
<p>The public key data, as specified in <cite>RFC 4880</cite>, is stored in a PEP data |
|
node. Note that OpenPGP's ASCII Armor is not used, instead the XMPP client MUST encode the |
|
public key using Base64. The id of the node MUST be "urn:xmpp:openpgp:0:public-keys:" followed |
|
by the fingerprint string of the OpenPGP public-key contained in the data node.</p> |
|
|
|
<p>In absence of a use-case specific access model, it is RECOMMENDED to use the 'open' access |
|
model for the public key data node in order to give entities without presence subscription |
|
read access to the public key.</p> |
|
|
|
<p>The access model can be changed efficiently by using publish-options.</p> |
|
|
|
<p>The <em>OpenPGP v4 fingerprint string</em> is obtained as follows: First the raw bytes of the |
|
fingerprint are computed as specified in <cite>RFC 4880 § 12.2.</cite>. Then the bytes are |
|
encoded as a hexadecimal string using upper case characters<note>This matches the representation |
|
used by GnuPG minus the SPACE separation.</note>.</p> |
|
|
|
<p> The publishing entity SHOULD set the PubSub item ID to the time the item is published encoded |
|
as DateTime format specified in <cite>XEP-0082</cite>.</p> |
|
|
|
<p>The data node MUST contain an <pubkey/> element qualified by the 'urn:xmpp:openpgp:0' |
|
namespace. The element MUST include a <data/> element which contains the data of the key |
|
Base64 encoded.</p> |
|
|
|
<example caption='Saving the public key in the data node.'><![CDATA[ |
|
<iq type='set' from='juliet@example.org/balcony' id='publish1'> |
|
<pubsub xmlns='http://jabber.org/protocol/pubsub'> |
|
<publish node='urn:xmpp:openpgp:0:public-keys:1357B01865B2503C18453D208CAC2A9678548E35'> |
|
<item id='2020-01-21T10:46:21Z'> |
|
<pubkey xmlns='urn:xmpp:openpgp:0'> |
|
<data> |
|
BASE64_OPENPGP_PUBLIC_KEY |
|
</data> |
|
</pubkey> |
|
</item> |
|
</publish> |
|
<publish-options> |
|
<x xmlns='jabber:x:data' type='submit'> |
|
<field var='FORM_TYPE' type='hidden'> |
|
<value>http://jabber.org/protocol/pubsub#publish-options</value> |
|
</field> |
|
<field var='pubsub#access_model'> |
|
<value>open</value> |
|
</field> |
|
</x> |
|
</publish-options> |
|
</pubsub> |
|
</iq>]]></example> |
|
|
|
</section2> |
|
|
|
<section2 topic='The OpenPGP Public Key Metadata Node' anchor='announcing-pubkey-list'> |
|
|
|
<p>To update the public keys used by an entity, the metadata node is updated. Before adding a |
|
OpenPGP key fingerprint to the metadata node, the publisher MUST ensure that the public key is available |
|
at the corresponding data node.</p> |
|
|
|
<p>Just like with the public key data node, in absence of a use-case specific access model, |
|
it is RECOMMENDED to set the access model of the metadata node to 'open', such that entities |
|
without mutual presence subscription are still able to access the node items.</p> |
|
|
|
<p> The ID of the metadata node is 'urn:xmpp:openpgp:0:public-keys'. It contains a |
|
<public-keys-list/> element qualified by the 'urn:xmpp:openpgp:0' namespace containing one |
|
or more <pubkey-metadata/> elements. Every pubkey-metadata element MUST have a |
|
'v4-fingerprint' attribute, containing the OpenPGP v4 fingerprint string, and a 'date' |
|
attribute, containing the time the key was published or updated in DateTime format of |
|
<cite>XEP-0082</cite>. An OpenPGP V4 fingerprint MUST NOT occur in the list more than once.</p> |
|
|
|
<example caption='Publishing a public key to the metadata node.'><![CDATA[ |
|
<iq type='set' from='juliet@example.org/balcony' id='publish1'> |
|
<pubsub xmlns='http://jabber.org/protocol/pubsub'> |
|
<publish node='urn:xmpp:openpgp:0:public-keys'> |
|
<item> |
|
<public-keys-list xmlns='urn:xmpp:openpgp:0'> |
|
<pubkey-metadata |
|
v4-fingerprint='1357B01865B2503C18453D208CAC2A9678548E35' |
|
date='2018-03-01T15:26:12Z' |
|
/> |
|
<pubkey-metadata |
|
v4-fingerprint='67819B343B2AB70DED9320872C6464AF2A8E4C02' |
|
date='1953-05-16T12:00:00Z' |
|
/> |
|
</public-keys-list> |
|
</item> |
|
</publish> |
|
<publish-options> |
|
<x xmlns='jabber:x:data' type='submit'> |
|
<field var='FORM_TYPE' type='hidden'> |
|
<value>http://jabber.org/protocol/pubsub#publish-options</value> |
|
</field> |
|
<field var='pubsub#access_model'> |
|
<value>open</value> |
|
</field> |
|
</x> |
|
</publish-options> |
|
</pubsub> |
|
</iq>]]></example> |
|
|
|
</section2> |
|
|
|
<section2 topic='Discovering Public Keys of a User' anchor='discover-pubkey-list'> |
|
|
|
<p>In order to discover the OpenPGP public keys of a remote entity, the interested entity first |
|
queries the remote entity's metadata note to learn about the currently annouced OpenPGP |
|
keys.</p> |
|
|
|
<example caption='Requesting the metadata node of a user.'><![CDATA[ |
|
<iq from='romeo@example.org/orchard' |
|
to='juliet@example.org' |
|
type='get' |
|
id='getmeta'> |
|
<pubsub xmlns='http://jabber.org/protocol/pubsub'> |
|
<items node='urn:xmpp:openpgp:0:public-keys'/> |
|
</pubsub> |
|
</iq>]]></example> |
|
|
|
<example caption='Personal Eventing Protocol result containing the metadata node of the user.'><![CDATA[ |
|
<iq from='juliet@example.org' |
|
to='romeo@example.org/orchard' |
|
type='result' |
|
id='getmeta'> |
|
<pubsub xmlns='http://jabber.org/protocol/pubsub'> |
|
<items node='urn:xmpp:openpgp:0:public-keys'> |
|
<item> |
|
<public-keys-list xmlns='urn:xmpp:openpgp:0'> |
|
<pubkey-metadata |
|
v4-fingerprint='1357B01865B2503C18453D208CAC2A9678548E35' |
|
date='2018-03-01T15:26:12Z' |
|
/> |
|
<pubkey-metadata |
|
v4-fingerprint='67819B343B2AB70DED9320872C6464AF2A8E4C02' |
|
date='1953-05-16T12:00:00Z' |
|
/> |
|
</public-keys-list> |
|
</item> |
|
</items> |
|
</pubsub> |
|
</iq>]]></example> |
|
|
|
</section2> |
|
|
|
<section2 topic='Requesting Public Keys' anchor='discover-pubkey'> |
|
|
|
<p>OpenPGP key(s) can be retrieved by querying the data node for a specific |
|
fingerprint.</p> |
|
|
|
<example caption='Requesting an OpenPGP public key from an XMPP entity.'><![CDATA[ |
|
<iq from='romeo@example.org/orchard' |
|
to='juliet@example.org' |
|
type='get' |
|
id='getpub'> |
|
<pubsub xmlns='http://jabber.org/protocol/pubsub'> |
|
<items node='urn:xmpp:openpgp:0:public-keys:1357B01865B2503C18453D208CAC2A9678548E35' |
|
max_items='1'/> |
|
</pubsub> |
|
</iq>]]></example> |
|
|
|
<example caption='Personal Eventing Protocol result containing the requested public key.'><![CDATA[ |
|
<iq from='juliet@example.org' |
|
to='romeo@example.org/orchard' |
|
type='result' |
|
id='getpub'> |
|
<pubsub xmlns='http://jabber.org/protocol/pubsub'> |
|
<items node='urn:xmpp:openpgp:0:public-keys:1357B01865B2503C18453D208CAC2A9678548E35'> |
|
<item id='2020-01-21T10:46:21Z'> |
|
<pubkey xmlns='urn:xmpp:openpgp:0'> |
|
<data> |
|
BASE64_OPENPGP_PUBLIC_KEY |
|
</data> |
|
</pubkey> |
|
</item> |
|
</items> |
|
</pubsub> |
|
</iq>]]></example> |
|
|
|
<p>Note that the result may contain multiple pubkey elements. Only |
|
the public keys found in the most recent item MUST be used. Requesters |
|
may want to limit the results to the most recent item using the |
|
'max_items' attribute set to '1'. Clients could alternatively use |
|
&xep0059; as an alternative to 'max_items' but accoding to |
|
<cite>XEP-0060</cite> RSM is not (yet) mandatory for PubSub |
|
services.</p> |
|
|
|
<p>Some XMPP services may not provide the Personal Eventing |
|
Protocol feature required to provide the mechanism described |
|
here. If so, they will return an &IQ; error of type |
|
service-unavailable.</p> |
|
|
|
</section2> |
|
|
|
<section2 topic='Receiving notifications about key changes' anchor='pubsub-notifications'> |
|
|
|
<p>Entities creating PEP nodes defined herein SHOULD configure the nodes as notification-only |
|
nodes by setting 'pubsub#deliver_payloads" configuration field to 'false'.</p> |
|
|
|
<p>Entities which are subscribed to the metadata node or advertise the |
|
"urn:xmpp:openpgp:0:public-keys+notify" feature via &xep0030; (see <cite>XEP-0060</cite> <link |
|
url='https://xmpp.org/extensions/xep-0060.html#filtered-notifications'>§ 9.2</link>) receive a |
|
notification upon a node update. Entities subscribed to PEP nodes defined herein MUST be prepared |
|
that PubSub notifications may be without the payload and only contain the published item's ID.</p> |
|
|
|
</section2> |
|
|
|
</section1> |
|
|
|
<section1 topic='Synchronizing the Secret Key with a Private PEP Node' anchor='synchro-pep'> |
|
|
|
<!-- |
|
TODO: Also split in metadata and data node? Probably not, because it could cause stale secret |
|
keys on the service (since it is not possible to list all PEP nodes starting with |
|
e.g. urn:xmpp:openpgp:0:secret-keys and delete old ones. We could split later anyways. |
|
--> |
|
|
|
<p>A private PEP node is used to allow XMPP clients to synchronize |
|
the user's secret OpenPGP key. Where private PEP node is defined: A |
|
PEP node in whitelist mode where only the bare JID of the key |
|
owner is whitelisted as described in &xep0223;. The secret key is |
|
additionally encrypted.</p> |
|
|
|
<section2 topic='Required PEP features' anchor='synchro-pep-requirements'> |
|
|
|
<p>The used PEP server MUST support PEP and the whitelist access |
|
model. It SHOULD also support persistent items.</p> |
|
|
|
<section3 topic='Discovering support' anchor='synchro-pep-discover-support'> |
|
|
|
<example caption='Account owner queries server regarding protocol support'><![CDATA[ |
|
<iq from='juliet@capulet.lit/balcony' |
|
to='juliet@capulet.lit' |
|
id='disco1' |
|
type='get'> |
|
<query xmlns='http://jabber.org/protocol/disco#info'/> |
|
</iq> |
|
]]></example> |
|
|
|
<p>The service discovery result must contain a PEP identity |
|
'<identity category='pubsub' type='pep'/>, and the |
|
'http://jabber.org/protocol/pubsub#access-whitelist' |
|
feature. Ideally it also contains the |
|
'http://jabber.org/protocol/pubsub#persistent-items' |
|
feature</p> |
|
|
|
<example caption='Server communicates protocol support'><![CDATA[ |
|
<iq from='juliet@capulet.lit' |
|
to='juliet@capulet.lit/balcony' |
|
id='disco1' |
|
type='result'> |
|
<query xmlns='http://jabber.org/protocol/disco#info'> |
|
<identity category='account' type='registered'/> |
|
<identity category='pubsub' type='pep'/> |
|
<feature var='http://jabber.org/protocol/pubsub#access-presence'/> |
|
<feature var='http://jabber.org/protocol/pubsub#auto-create'/> |
|
<feature var='http://jabber.org/protocol/pubsub#auto-subscribe'/> |
|
<feature var='http://jabber.org/protocol/pubsub#config-node'/> |
|
<feature var='http://jabber.org/protocol/pubsub#create-and-configure'/> |
|
<feature var='http://jabber.org/protocol/pubsub#create-nodes'/> |
|
<feature var='http://jabber.org/protocol/pubsub#filtered-notifications'/> |
|
<feature var='http://jabber.org/protocol/pubsub#persistent-items'/> |
|
<feature var='http://jabber.org/protocol/pubsub#publish'/> |
|
<feature var='http://jabber.org/protocol/pubsub#retrieve-items'/> |
|
<feature var='http://jabber.org/protocol/pubsub#subscribe'/> |
|
... |
|
</query> |
|
</iq>]]></example> |
|
|
|
</section3> |
|
|
|
</section2> |
|
|
|
<section2 topic='Requesting Information About the Secret Key PEP Node' anchor='req-info-secret-pep-node'> |
|
|
|
<p>In order to synchronize the secret key over a private PEP node, |
|
clients first need to discover and verify the node for the correct |
|
settings.</p> |
|
|
|
<section3 topic='Client Sends Request' anchor='client-sends-secret-request'> |
|
|
|
<example caption='Requesting the user's secret key.'><![CDATA[ |
|
<iq from='romeo@example.org/orchard' |
|
to='juliet@example.org' |
|
type='get' |
|
id='getsecret'> |
|
<pubsub xmlns='http://jabber.org/protocol/pubsub'> |
|
<items node='urn:xmpp:openpgp:0:secret-key' |
|
max_items='1'/> |
|
</pubsub> |
|
</iq>]]></example> |
|
|
|
</section3> |
|
<section3 topic='PEP Service Success Response' anchor='client-receives-secret-response'> |
|
|
|
<example caption='Personal Eventing Protocol result containing the requested secret key.'><![CDATA[ |
|
<iq from='juliet@example.org' |
|
to='romeo@example.org/orchard' |
|
type='result' |
|
id='getsecret'> |
|
<pubsub xmlns='http://jabber.org/protocol/pubsub'> |
|
<items node='urn:xmpp:openpgp:0:secret-key'> |
|
<item> |
|
<secretkey xmlns='urn:xmpp:openpgp:0'> |
|
BASE64_OPENPGP_ENCRYPTED_SECRET_KEY |
|
</secretkey> |
|
</item> |
|
</items> |
|
</pubsub> |
|
</iq>]]></example> |
|
|
|
</section3> |
|
<section3 topic='PEP Node Does Not Exist Response' anchor='error-pep-node-inexistent'> |
|
|
|
<p>If the node does not exist the service will return an &IQ; |
|
error indicating the item-not-found error condition. The |
|
client MUST then create it with an whitelist access model.</p> |
|
|
|
<example caption='Node does not exist'><![CDATA[ |
|
<iq from='juliet@example.org' |
|
to='romeo@example.org/orchard' |
|
type='error' |
|
id='getsecret'> |
|
<error type='cancel'> |
|
<item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> |
|
</error> |
|
</iq>]]></example> |
|
|
|
</section3> |
|
|
|
<section3 topic='PEP Not Supported' anchor='pep-not-supported'> |
|
|
|
<p>The service will return a service-unavailable error &IQ; if |
|
it does not support PEP.</p> |
|
|
|
<example caption='Node does not exist'><![CDATA[ |
|
<iq from='juliet@example.org' |
|
to='romeo@example.org/orchard' |
|
type='error' |
|
id='getsecret'> |
|
<error type='cancel'> |
|
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> |
|
</error> |
|
</iq>]]></example> |
|
|
|
</section3> |
|
|
|
</section2> |
|
<section2 topic='Creating the Secret Key PEP Node' anchor='create-secret-node'> |
|
|
|
<example caption='Client creates secret key PEP node'><![CDATA[ |
|
<iq type='set' |
|
from='juliet@example.org/balcony' |
|
id='create-node'> |
|
<pubsub xmlns='http://jabber.org/protocol/pubsub'> |
|
<create node='urn:xmpp:openpgp:0:secret-key'/> |
|
<configure> |
|
<x xmlns='jabber:x:data' type='submit'> |
|
<field var='FORM_TYPE' type='hidden'> |
|
<value>http://jabber.org/protocol/pubsub#node_config</value> |
|
</field> |
|
<field var='pubsub#access_model'> |
|
<value>whitelist</value> |
|
</field> |
|
<field var='pubsub#send_last_published_item'> |
|
<value>on_sub</value> |
|
</field> |
|
</x> |
|
</configure> |
|
</pubsub> |
|
</iq>]]></example> |
|
<example caption='Service informs requesting entity of success'><![CDATA[ |
|
<iq type='result' |
|
to='juliet@example.org/balcony' |
|
id='create-node'/>]]></example> |
|
|
|
<p>The node is now created and the only affiliated entity is the |
|
bare JID of the user, who created the node, with an affiliation as |
|
'owner'.</p> |
|
|
|
</section2> |
|
|
|
<section2 topic='Encrypting the Secret Key Backup' anchor='backup-encryption'> |
|
|
|
<p>In order to set a new secret key, clients store the encrypted |
|
secret key as Base64 encoded raw OpenPGP message within an |
|
<secretkey/> element qualified by the 'urn:xmpp:openpgp:0' |
|
namespace. These secret key backups are created as follows:</p> |
|
|
|
<ol> |
|
<li>All secret keys that should be included in the backup MUST |
|
be concatenated in their transferable key format (<cite>RFC |
|
4880</cite> <link |
|
url='http://tools.ietf.org/html/rfc4880#section-11.2'>§ |
|
11.1</link>). The octet indicating string-to-key usage conventions |
|
MUST be set to zero in the corresponding Secret-Key Packet(s) |
|
(<cite>RFC 4880</cite> <link url='https://tools.ietf.org/html/rfc4880#section-5.5.3'>§ 5.5.3</link>). |
|
The secret key material will be encrypted in step 4 using a |
|
Symmetric-Key Encrypted Session Key Packet. |
|
</li> |
|
<li>A backup code is generated from secure random: The backup |
|
code consists of 24 upper case characters from the Latin |
|
alphabet and numbers without 'O' ("LATIN CAPITAL LETTER O") |
|
and '0' ("DIGIT ZERO") (alphabet: |
|
<tt>123456789ABCDEFGHIJKLMNPQRSTUVWXYZ</tt>) grouped into |
|
4-character chunks, e.g., |
|
<tt>TWNK-KD5Y-MT3T-E1GS-DRDB-KVTW</tt>. The characters MUST be |
|
generated from cryptographically secure random. For example |
|
<tt><link |
|
url='https://lwn.net/Articles/606141/'>getrandom(2)</link></tt>, |
|
<tt><link |
|
url='https://docs.oracle.com/javase/8/docs/api/java/security/SecureRandom.html'>SecureRandom</link></tt> |
|
or <tt>/dev/urandom</tt>. More information about the |
|
randomness requirements for security can be found in &rfc4086; |
|
</li> |
|
<li>The whole backup code including the dashes is directly |
|
used as a string to encrypt the concatenated transferable keys |
|
as an OpenPGP message. More precisely: It is used as the |
|
symmetric-key for a Symmetric-Key Encrypted Session Key Packet |
|
according to <cite>RFC 4880</cite> <link |
|
url='http://tools.ietf.org/html/rfc4880#section-5.3'>§ |
|
5.3</link>; the symmetric-key is thus 29 characters long |
|
including the dashes. The encryption algorithm MUST be one of |
|
the standardized OpenPGP symmetric algorithms, e.g, AES-128. |
|
</li> |
|
</ol> |
|
|
|
</section2> |
|
|
|
</section1> |
|
|
|
<section1 topic='Business Rules' anchor='rules'> |
|
|
|
<section2 topic='OpenPGP Packet Format Version Restriction' anchor='openpgp-packet-format-version'> |
|
|
|
<p>Implementations of this XEP MUST generate and accept only |
|
version 4 (or higher) OpenPGP packets. Lower version OpenPGP |
|
packets are insecure in many aspects (see for example <cite>RFC |
|
4880</cite> <link |
|
url='http://tools.ietf.org/html/rfc4880#section-5.5.2'>§ |
|
5.5.2</link>.).</p> |
|
|
|
</section2> |
|
|
|
<section2 topic='PubSub Node Configuration' anchor='pubsub-node-configuration'> |
|
|
|
<p>The Public-Key <em>metadata</em> node and the Secret-Key node SHOULD be configured to either |
|
never send the latest item, or to send the latest item only when a new entity subscribed. Thus |
|
the nodes 'send_last_published_item' configuration option SHOULD be set to either 'never' or |
|
'on_sub' (see <cite>XEP-0060</cite> <link |
|
url='https://xmpp.org/extensions/xep-0060.html#registrar-formtypes-config'>§ 16.4.4</link>).</p> |
|
|
|
</section2> |
|
|
|
<section2 topic='Key Enforcement' anchor='key-enforcement'> |
|
|
|
<p>Whenever an entity becomes aware that the metadata node has changed (e.g., by receiving a PEP |
|
update from their own account), it SHOULD check that the list contains the key they use. If the |
|
key has been removed, the entity SHOULD reannounce it.</p> |
|
|
|
</section2> |
|
|
|
</section1> |
|
|
|
<section1 topic='Implementors Advice' anchor='implementors-advice'> |
|
|
|
<section2 topic='Design Principles and Techniques' anchor='design-and-techniques'> |
|
|
|
<p>OpenPGP implementations have a sad history of being not very |
|
user-friendly which results in users either not using OpenPGP or in |
|
users wrongly using OpenPGP. Implementors of this XEP, and |
|
additional future XEPs based on this XEP, therefore should read |
|
<span class='ref'><link |
|
url='http://g10code.com/steed.html'>STEED</link></span><note>Koch, |
|
Werner, and Marcus Brinkman "STEED — Usable End-to-End |
|
Encryption", White Paper, g10 GmbH, 2011-10-17. <<link |
|
url='http://g10code.com/steed.html'>http://g10code.com/steed.html</link>></note> |
|
and <span class='ref'><link |
|
url='https://www.cs.berkeley.edu/~tygar/papers/Why_Johnny_Cant_Encrypt/OReilly.pdf'>"Why |
|
Johnny can't encrypt"</link></span><note>Whitten, Alma, and |
|
J. Doug Tygar. "Why Johnny Can't Encrypt: A Usability Evaluation |
|
of PGP 5.0." Usenix Security. Vol. 1999. 1999. <<link |
|
url='https://www.cs.berkeley.edu/~tygar/papers/Why_Johnny_Cant_Encrypt/OReilly.pdf'>https://www.cs.berkeley.edu/~tygar/papers/Why_Johnny_Cant_Encrypt/OReilly.pdf</link>></note>. Implementors |
|
of this XEP are encouraged to provide the concepts described in |
|
STEED:</p> |
|
|
|
<ul> |
|
<li>Automatic key generation</li> |
|
<li>Automatic key distribution</li> |
|
<li>Opportunistic encryption</li> |
|
<li>Trust upon first contact</li> |
|
</ul> |
|
|
|
<p>Furthermore implementors should design the user interface for |
|
effective security by following the design principles and |
|
techniques for security mentioned in "Why Johnny Can't |
|
Encrypt".</p> |
|
|
|
</section2> |
|
|
|
<section2 topic='Stanza Size' anchor='stanza-size'> |
|
|
|
<p>Implementors should be aware that the size OpenPGP public and |
|
secret keys is somewhere in the range of tens of |
|
kilobytes. Applying Base64 encoding on keys, as it is described |
|
herein, further increases the size. The formula to determine the |
|
Base64 encoded size is: ceil(bytes / 3) * 4. Thus the lower bound |
|
for the maximum stanza size of 10000 bytes, as specified in <cite>RFC |
|
6120</cite> § 13.12. 4., is usually exceeded. However all XMPP server |
|
implementations, the authors are aware of, follow the |
|
recommendation of the RFC and do not blindly set the maximum |
|
stanza size to such a low value, but use a much higher |
|
threshold. Therefore, this should hardly be an issue for |
|
implementations. Nevertheless, it is advised to keep the size of |
|
OpenPGP keys small by removing all signatures except the most |
|
recent self-signature on each User ID before exporting the key |
|
(cf. GnuPG's <tt>--export-options export-minimal</tt>). |
|
In addition, implementors are advised to handle |
|
<policy-violation/> error responses when trying to |
|
transmit Base64 encoded keys.</p> |
|
|
|
</section2> |
|
|
|
<section2 topic='XMPP Address Normalization' anchor='xmpp-address-normalization'> |
|
|
|
<p>The format of XMPP addresses, sometimes called JIDs, is well |
|
defined. Thus they need to be normalized, as defined in |
|
&rfc7622;. When implementations are required to compare XMPP |
|
addresses for equality, as it is the case in <link |
|
url='#openpgp-verification'>"Verification of &openpgp; |
|
Content"</link>, then they also have to compare the normalized |
|
versions of the addresses.</p> |
|
|
|
</section2> |
|
|
|
</section1> |
|
|
|
<section1 topic='Rationale' anchor='rationale'> |
|
|
|
<section2 topic='Key Handling' anchor='key-handling'> |
|
|
|
<p>This specification intentionally does not specify if the used |
|
OpenPGP key should be a primary key or a subkey. It is even |
|
possible to announce multiple public keys in the Personal Eventing |
|
Protocol node. Implementations MUST be prepared to find multiple |
|
public keys. The authors however believe that for ease of use only |
|
one OpenPGP key specially crafted for the XMPP use case should be |
|
created, announced and used.</p> |
|
|
|
</section2> |
|
|
|
<section2 topic='OpenPGP Element and Content Element Design' anchor='openpgp-element-design'> |
|
|
|
<p>The &openpgp; and OpenPGP content elements are container |
|
elements for arbitrary signed and encrypted data and can thus act |
|
as building blocks for encrypted data included in Message, IQ and |
|
Presence stanzas. For example, future specifications may use them |
|
to implement encrypted versions of &xep0047; or &xep0261;.</p> |
|
|
|
<p>Note that signed OpenPGP messages already contain a timestamp |
|
as per the OpenPGP specification. OpenPGP content elements |
|
nevertheless require the 'time' element because not every OpenPGP |
|
API may provide access to the embedded OpenPGP timestamp.</p> |
|
|
|
<p>The 'rpad' element of the OpenPGP content elements exists to |
|
prevent length-based side channel attacks.</p> |
|
|
|
</section2> |
|
|
|
<section2 topic='Addressing the Issues and Problems of XEP-0027' anchor='solving-xep0027-issues'> |
|
|
|
<p>This specification addresses all relevant issues of &xep0027; |
|
(<link url='https://xmpp.org/extensions/xep-0027.html#security'>§ |
|
4</link>, <link |
|
url='https://xmpp.org/extensions/xep-0027.html#issues'>§ |
|
5</link>). It mitigates replay attacks by including the |
|
recipient's address and a timestamp in the OpenPGP content |
|
element<note>Full Replay attack prevention would require a |
|
counter based approach.</note>. It allows for both, signing and |
|
encrypting of the element. The scope of the specification was |
|
deliberately limited to OpenPGP.</p> |
|
|
|
<p>Features like signed presences, which is provided by <cite>XEP-0027</cite>, |
|
may be added later on as add-on XEP to this.</p> |
|
|
|
</section2> |
|
|
|
<section2 topic='Not using OpenPGP ASCII Armor' anchor='openpgp-ascii-armor'> |
|
|
|
<p>We decided against OpenPGP ASCII Armor (which contains an |
|
additional checksum) and in favor for Base64, because |
|
encoding should be part of the network application rather than the |
|
crypto layer. Also XMPP, needs no additional error correction of payload. |
|
In "MIME Security with OpenPGP" (&rfc3156;), ASCII Armor |
|
has only been chosen to be backwards compatible with legacy applications |
|
supporting non-MIME OpenPGP emails only.</p> |
|
|
|
</section2> |
|
|
|
<section2 topic='OpenPGP User IDs' anchor='openpgp-user-ids'> |
|
|
|
<p>OpenPGP User IDs normally consist of a name - email address pair, e.g., |
|
"Juliet <juliet@example.org>" (<cite>RFC 4880</cite> <link |
|
url='http://tools.ietf.org/html/rfc4880#section-5.11'>§ 5.11</link>). |
|
For this XEP, we require User IDs of the format "xmpp:juliet@example.org". |
|
First, it is required to have at least one User ID indicating the use |
|
of this OpenPGP key. When doing certification of keys (key signing), |
|
the partner must know what User ID she actually certifies. |
|
Second, this format uses the standardized URI from <cite>XEP-0147</cite> to indicate |
|
that this User ID corresponds to a key that is used for XMPP. |
|
Third, having the Real Name inside provides no additional security |
|
or guideline if this key should be certified. The XMPP address |
|
is the only trust anchor here.</p> |
|
|
|
</section2> |
|
|
|
</section1> |
|
|
|
<section1 topic='Security Considerations' anchor='security'> |
|
|
|
<p>The scope of this XEP is intentionally limited, so that the |
|
specification just defines way for XMPP entities to discover, |
|
announce and synchronize OpenPGP keys, and how to exchange signed |
|
and encrypted data between two or more parties. Everything else is |
|
outside its scope. For example, how 'secure' the key material is |
|
protected on the endpoints is up to the implementation.</p> |
|
|
|
<p>And while this XEP specifies a mechanism how to discover and |
|
retrieve a public key, it does not define how the trust relation to |
|
this key should be established. Even if key discovery and retrieval |
|
over XMPP provides a stronger coupling between the possessing entity |
|
(the XMPP address) and the key, as compared to the OpenPGP keyservers, |
|
how a XMPP server authenticates a remote server is a server policy, |
|
which does vary from server to server. Implementation MUST provide a |
|
way for the user to establish and assign trust to a public key. For |
|
example by using a QR code shown on the recipient's device screen.</p> |
|
|
|
<p>Besides the protocol defined herein, OpenPGP implementations are |
|
another big attack surface. Needless to say that the security of |
|
encrypted data exchanged using this protocol depends on the security |
|
of the used OpenPGP implementation. It is strongly RECOMMENED to use |
|
existing implementations instead of writing your own. OpenPGP |
|
implementations have suffered from various vulnerabilities in the past |
|
which opened up DoS attack vectors. For example <link |
|
url='https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-4402'>CVE-2013-4402</link> |
|
and <link |
|
url='https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-4617'>CVE-2014-4717</link>.</p> |
|
|
|
</section1> |
|
|
|
<section1 topic='IANA Considerations' anchor='iana'> |
|
|
|
<p>This document requires no interaction with &IANA;.</p> |
|
|
|
</section1> |
|
|
|
<section1 topic='XMPP Registrar Considerations' anchor='registrar'> |
|
|
|
<section2 topic='Protocol Namespaces' anchor='registrar-ns'> |
|
|
|
<p>The ®ISTRAR; includes 'urn:xmpp:openpgp:0' in its registry of protocol namespaces (see &NAMESPACES;).</p> |
|
|
|
</section2> |
|
|
|
</section1> |
|
|
|
<section1 topic='XML Schema' anchor='schema'> |
|
|
|
<p>TODO: Add after the XEP leaves the 'experimental' state.</p> |
|
|
|
</section1> |
|
|
|
<section1 topic='Acknowledgements' anchor='acknowledgements'> |
|
|
|
<p>Thanks to Emmanuel Gil Peyrot, Sergei Golovan, Marc Laporte, Georg |
|
Lukas, Adithya Abraham Philip, Brian Cully, fiaxh, Paul Schaub, |
|
Philipp Hörist and Stefan Kropp for their feedback.</p> |
|
|
|
<p>The first draft of this specification was worked out and written |
|
on the wall of the 'Kymera' room in one of Google's buildings by the |
|
authors, consisting of members of the XMPP Standards Foundation and |
|
the OpenKeychain project, at the GSOC Mentors Summit 2015. The |
|
authors would like to thank Google for making it possible by |
|
bringing the right people together.</p> |
|
|
|
</section1> |
|
</xep> |
|
|
|
<!-- Local Variables: --> |
|
<!-- fill-column: 100 --> |
|
<!-- indent-tabs-mode: nil --> |
|
<!-- End: -->
|
|
|