<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type='text/xsl' href='xep.xsl'?>
<xep xmlns="">
<header>
  <title>Occupant identifiers for semi-anonymous MUCs</title>
  <abstract>This specification defines a method that allows clients to identify a MUC participant across reconnects and renames. It thus prevents impersonification of semi-anonymous users.</abstract>
  
<legal>
<copyright>This XMPP Extension Protocol is copyright © 1999 – 2024 by the <link url="https://xmpp.org/">XMPP Standards Foundation</link> (XSF).</copyright>
<permissions>Permission is hereby granted, free of charge, to any person obtaining a copy of this specification (the "Specification"), to make use of the Specification without restriction, including without limitation the rights to implement the Specification in a software program, deploy the Specification in a network service, and copy, modify, merge, publish, translate, distribute, sublicense, or sell copies of the Specification, and to permit persons to whom the Specification is furnished to do so, subject to the condition that the foregoing copyright notice and this permission notice shall be included in all copies or substantial portions of the Specification. Unless separate permission is granted, modified works that are redistributed shall not contain misleading information regarding the authors, title, number, or publisher of the Specification, and shall not claim endorsement of the modified works by the authors, any organization or project to which the authors belong, or the XMPP Standards Foundation.</permissions>
<warranty>## NOTE WELL: This Specification is provided on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. ##</warranty>
<liability>In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall the XMPP Standards Foundation or any author of this Specification be liable for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising from, out of, or in connection with the Specification or the implementation, deployment, or other use of the Specification (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if the XMPP Standards Foundation or such author has been advised of the possibility of such damages.</liability>
<conformance>This XMPP Extension Protocol has been contributed in full conformance with the XSF's Intellectual Property Rights Policy (a copy of which can be found at &lt;<link url="https://xmpp.org/about/xsf/ipr-policy">https://xmpp.org/about/xsf/ipr-policy</link>&gt; or obtained by writing to XMPP Standards Foundation, P.O. Box 787, Parker, CO 80134 USA).</conformance>
</legal>
  <number>0421</number>
  <status>Draft</status>
  <lastcall>2025-01-06</lastcall>
  <type>Standards Track</type>
  <sig>Standards</sig>
  <approver>Council</approver>
  <dependencies>
    <spec>XMPP Core</spec>
    <spec>XEP-0001</spec>
    <spec>XEP-0030</spec>
    <spec>XEP-0045</spec>
  </dependencies>
  <supersedes/>
  <supersededby/>
  <shortname>occupant-id</shortname>
  
  <author>
    <firstname>Marvin</firstname>
    <surname>Wißfeld</surname>
    <email>xmpp@larma.de</email>
    <jid>jabber@larma.de</jid>
  </author>

  <revision>
    <version>1.0.1</version>
    <date>2025-04-09</date>
    <initials>XEP Editor (dg)</initials>
    <remark>Fixed typo</remark>
  </revision>
  <revision>
    <version>1.0.0</version>
    <date>2025-01-15</date>
    <initials>XEP Editor (dg)</initials>
    <remark>Accept as Stable as per Council Vote from 2025-01-14.</remark>
  </revision>
  <revision>
    <version>0.3.0</version>
    <date>2025-01-07</date>
    <initials>lmw</initials>
    <remark><ul>
      <li>Adjust wording to use semi-anonymous or pseudonymous instead of anonymous</li>
      <li>Explicitly mention issues arising from occupant id matching across rooms</li>
      <li>Add example with server secret instead of room secret</li>
      <li>Add some pseudocode</li>
    </ul></remark>
  </revision>
  <revision>
    <version>0.2.0</version>
    <date>2024-05-28</date>
    <initials>mw</initials>
    <remark><ul>
      <li>Make explicit that one can't just hash the real JID.</li>
      <li>Expand security considerations.</li>
      <li>Add schema.</li>
      <li>Fix some examples captions and casing</li>
    </ul></remark>
  </revision>
  <revision>
    <version>0.1.0</version>
    <date>2019-08-20</date>
    <initials>XEP Editor (jsc)</initials>
    <remark>Accepted by vote of Council on 2019-07-17.</remark>
  </revision>
  <revision>
    <version>0.0.1</version>
    <date>2019-07-13</date>
    <initials>mw</initials>
    <remark><p>First draft.</p></remark>
  </revision>
</header>
<section1 topic="Introduction" anchor="intro">
  <p>
    <span class="ref"><link url="https://xmpp.org/extensions/xep-0045.html">Multi-User Chat (XEP-0045)</link></span> <note>XEP-0045: Multi-User Chat &lt;<link url="https://xmpp.org/extensions/xep-0045.html">https://xmpp.org/extensions/xep-0045.html</link>&gt;.</note> allows the creation of semi-anonymous multi-user text chats where
    the real JID of a occupant can not be discovered by other MUC
    occupants except moderators. As such users can freely join with using
    any identity of their choice, allowing to impersonate users while they are
    not online.
  </p>
  <p>
    With recent standard extensions, it becomes more relevant to be able to
    know if the occupant that sends one message is the same as the sender
    of another message, for example for <span class="ref"><link url="https://xmpp.org/extensions/xep-0308.html">Last Message Correction (XEP-0308)</link></span> <note>XEP-0308: Last Message Correction &lt;<link url="https://xmpp.org/extensions/xep-0308.html">https://xmpp.org/extensions/xep-0308.html</link>&gt;.</note>. At the same time it becomes
    harder for clients to determine this, for example due to the use of <span class="ref"><link url="https://xmpp.org/extensions/xep-0313.html">Message Archive Management (XEP-0313)</link></span> <note>XEP-0313: Message Archive Management &lt;<link url="https://xmpp.org/extensions/xep-0313.html">https://xmpp.org/extensions/xep-0313.html</link>&gt;.</note>
    with MUCs.
  </p>
  <p>
    This specification defines a method to combat issues arising out of the
    pseudonymity of MUC occupants while at the same time ensuring their privacy
    by not revealing their real JID to other occupants.
  </p>
</section1>
<section1 topic="Discovering support" anchor="disco">
  <p>
    If a MUC room implements occupant identifiers, it MUST
    specify the 'urn:xmpp:occupant-id:0' feature in its service discovery
    information features as specified in <span class="ref"><link url="https://xmpp.org/extensions/xep-0030.html">Service Discovery (XEP-0030)</link></span> <note>XEP-0030: Service Discovery &lt;<link url="https://xmpp.org/extensions/xep-0030.html">https://xmpp.org/extensions/xep-0030.html</link>&gt;.</note>.
  </p>
  <example caption="Client requests information about a MUC"><![CDATA[
<iq type='get'
    to='coven@chat.shakespeare.lit'
    from='hag66@shakespeare.lit/pda'
    id='info1'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>]]></example>
  <example caption="MUC advertises support for occupant identifiers"><![CDATA[
<iq type='result'
    to='hag66@shakespeare.lit/pda'
    from='coven@chat.shakespeare.lit'
    id='info1'>
  <query xmlns='http://jabber.org/protocol/disco#info'>
...
    <feature var='urn:xmpp:occupant-id:0'/>
...
  </query>
</iq>]]></example>
</section1>
<section1 topic="Use Cases" anchor="usecases">
  <section2 topic="Entering a Room" anchor="enter">
    <p>
      When a user enters a room, they send a presence to claim the nickname in
      the MUC. A MUC that supports occupant identifiers
      attaches an &lt;occupant-id&gt; element within the "urn:xmpp:occupant-id:0"
      namespace to the presence sent to all occupants in the room.
    </p>
    <example caption="Client joins a room"><![CDATA[
<presence
    from='hag66@shakespeare.lit/pda'
    id='n13mt3l'
    to='coven@chat.shakespeare.lit/thirdwitch'>
  <x xmlns='http://jabber.org/protocol/muc'/>
</presence>]]></example>
    <example caption="Service sends new occupant's presence to all occupants"><![CDATA[
<presence
    from='coven@chat.shakespeare.lit/thirdwitch'
    id='27C55F89-1C6A-459A-9EB5-77690145D624'
    to='crone1@shakespeare.lit/desktop'>
  <x xmlns='http://jabber.org/protocol/muc#user' />
  <occupant-id xmlns="urn:xmpp:occupant-id:0" id="dd72603deec90a38ba552f7c68cbcc61bca202cd" />
</presence>]]></example>
  </section2>
    <section2 topic="Sending a Message to All Occupants" anchor="message">
      <p>
        An occupant sends a message to all other occupants in the room by
        sending a message of type "groupchat" to the &lt;room@service&gt;. A MUC
        supporting occupant identifiers attaches an
        &lt;occupant-id&gt; element within the "urn:xmpp:occupant-id:0" to the
        message sent to all occupants in the room.
      </p>
    <example caption="Client sends a message to all occupants"><![CDATA[
<message
    from='hag66@shakespeare.lit/pda'
    id='hysf1v37'
    to='coven@chat.shakespeare.lit'
    type='groupchat'>
  <body>Harpier cries: 'tis time, 'tis time.</body>
</message>]]></example>
    <example caption="Service reflects message to all occupants"><![CDATA[
<message
    from='coven@chat.shakespeare.lit/thirdwitch'
    id='hysf1v37'
    to='crone1@shakespeare.lit/desktop'
    type='groupchat'>
  <body>Harpier cries: 'tis time, 'tis time.</body>
  <occupant-id xmlns="urn:xmpp:occupant-id:0" id="dd72603deec90a38ba552f7c68cbcc61bca202cd" />
</message>]]></example>
  </section2>
</section1>
<section1 topic="Business Rules" anchor="rules">
  <p>
    Messages and presences MUST NOT contain more than one &lt;occupant-id&gt;
    element. If the message or presence received by the MUC service already
    contains &lt;occupant-id&gt; element, the MUC service MUST replace such
    element before reflecting the message or presence including it.
  </p>
  <p>
    The &lt;occupant-id&gt; element MUST be attached to every message and every
    presence sent by a MUC. This includes messages sent as part of the
    discussion history after joining a room, requested via <span class="ref"><link url="https://xmpp.org/extensions/xep-0313.html">Message Archive Management (XEP-0313)</link></span> <note>XEP-0313: Message Archive Management &lt;<link url="https://xmpp.org/extensions/xep-0313.html">https://xmpp.org/extensions/xep-0313.html</link>&gt;.</note> or any
    other means.
  </p>
  <p>
    The &lt;occupant-id&gt; element MUST be ignored if support for the feature
    is not announced via <span class="ref"><link url="https://xmpp.org/extensions/xep-0030.html">Service Discovery (XEP-0030)</link></span> <note>XEP-0030: Service Discovery &lt;<link url="https://xmpp.org/extensions/xep-0030.html">https://xmpp.org/extensions/xep-0030.html</link>&gt;.</note>, as malicious clients might forge occupant
    identifiers if the room does not support them.
  </p>
  <p>
    A MUC service MAY allow the administrator to enable or disable occupant
    identifiers on a per-room basis. If occupant identifiers are force enabled
    for all rooms on a MUC service, it SHOULD additionally specify the 
    'urn:xmpp:occupant-id:0' feature on the MUC service. It MUST NOT specify
    the feature on the service otherwise.
  </p>
  <section2 topic="Occupant ID generation" anchor="id-generation">
    <p>
      The occupant identifier MUST be generated such that it is stable. This
      means that if a user joins the same room a second time, the occupant
      identifier MUST be the same as was assigned the first time. A user in
      the sense of this specification is identified by its real bare JID.
    </p>
    <p>
      The occupant identifier MUST be generated such that it is unique. This
      means that it MUST be sufficiently improbable that one user is able to
      re-create the occupant identifier of another user.
    </p>
    <p>
      The occupant identifier MUST be generated such that it is pseudonymous. This
      means that it MUST be sufficiently hard to determine the real bare JID of
      an occupant from its occupant identifier. Additionally, a MUC service
      SHOULD generate the identifier such that the occupant identifier of a user
      in one room of the service does not match the occupant identifier of the
      same user in another room of the same service.
      If the MUC service generates the same occupant identifier for the same
      user in different rooms, information shared using different nicknames and
      in different rooms could be combined through the occupant identifier and
      thereby unintentionally reveal information about the user.
      To guarantee the pseudonymity property, the server MUST NOT generate an
      occupant identifier by only hashing the real bare JID using static,
      guessable or discoverable parameters.
    </p>
    <p>
      The occupant identifier MUST have a maximum length of 128 characters. The
      recipient MUST consider the occupant identifier to be an opaque string.
    </p>
    <p>
      One way to ensure these properties is to generate a private secret key for
      every room and use an HMAC algorithm with a sufficiently secure hash
      function to generate the occupant identifier from the real bare JID and
      that secret key.
      Alternatively, the service can generate a single private secret key for the
      whole service and use an HMAC algorithm to generate the occupant identifier
      from the real bare JID, the room bare JID and the service secret key.
      This procedure ensures all the required properties with
      minimal server side storage requirements.
    </p>
    <example caption="Pseudo-code of occupant identifier generation"><![CDATA[
room_secret_key := RANDOM_BYTES(32) # Stored with room state
occupant-id := HEX(HMAC_SHA1(room_secret_key, user_bare_jid))

# or #

service_secret_key := RANDOM_BYTES(32) # Stored with service state
occupant-id := HEX(HMAC_SHA1(service_secret_key, room_bare_jid || NUL || user_bare_jid))
]]></example>
  </section2>
</section1>
<section1 topic="Security Considerations" anchor="security">
  <p>
    If a MUC uses occupant identifiers, nickname changes will be visible to
    all occupants of the room. Clients may warn users about this circumstance
    before joining the room or when changing the nickname.
  </p>
  <p>
    When the MUC service does not support this specification, the server will
    likely forward any &lt;occupant-id&gt; included in &lt;message&gt;s sent by
    other room occupants and reflected by the MUC service. Receiving clients
    must be careful to only process occupant identifiers if the MUC server
    advertises support for this specification as described in the
    <link url="#disco">Discovering support</link> section.
  </p>
  <p>
    The pseudonymity property of occupant identifiers is crucial to not
    accidentally reveal an occupant's real bare JID to other room occupants.
    Specifically, a simple hash over the occupant's real bare JID is not
    sufficient as an occupant identifier, as unsalted hashes can be reversed
    easily based on a dictionary of candidate JIDs. Review the
    <link url="#id-generation">Occupant ID generation</link> section for more
    details.
  </p>
</section1>
<section1 topic="IANA Considerations" anchor="iana">
  <p>This document requires no interaction with the <span class="ref"><link url="http://www.iana.org/">Internet Assigned Numbers Authority (IANA)</link></span> <note>The Internet Assigned Numbers Authority (IANA) is the central coordinator for the assignment of unique parameter values for Internet protocols, such as port numbers and URI schemes. For further information, see &lt;<link url="http://www.iana.org/">http://www.iana.org/</link>&gt;.</note>.</p>
</section1>
<section1 topic="XMPP Registrar Considerations" anchor="registrar">
  <section2 topic="Protocol Namespaces" anchor="ns">
    <p>The <span class="ref"><link url="https://xmpp.org/registrar/">XMPP Registrar</link></span> <note>The XMPP Registrar maintains a list of reserved protocol namespaces as well as registries of parameters used in the context of XMPP extension protocols approved by the XMPP Standards Foundation. For further information, see &lt;<link url="https://xmpp.org/registrar/">https://xmpp.org/registrar/</link>&gt;.</note> includes 'urn:xmpp:occupant-id:0' in its registry of protocol namespaces (see &lt;<link url="https://xmpp.org/registrar/namespaces.html">https://xmpp.org/registrar/namespaces.html</link>&gt;).</p>
    <ul>
      <li>urn:xmpp:occupant-id:0</li>
    </ul>
  </section2>
</section1>
<section1 topic="XML Schema">
  <code><![CDATA[
<?xml version='1.0' encoding='utf-8'?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="urn:xmpp:occupant-id:0"
           xmlns="urn:xmpp:occupant-id:0"
           elementFormDefault="qualified">

    <xs:element name="occupant-id">
      <xs:complexType>
        <xs:attribute name="id" type="OccupantIdentifier" use="required" />
      </xs:complexType>
    </xs:element>

    <xs:simpleType name="OccupantIdentifier">
        <xs:restriction base="xs:string">
            <xs:minLength value="1"/>
            <xs:maxLength value="128"/>
        </xs:restriction>
    </xs:simpleType>

</xs:schema>
]]></code>
</section1>
</xep>
