ExplicitMessageEncryptionElement.java

  1. /**
  2.  *
  3.  * Copyright 2017-2019 Florian Schmaus
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.eme.element;

  18. import java.util.HashMap;
  19. import java.util.List;
  20. import java.util.Map;

  21. import javax.xml.namespace.QName;

  22. import org.jivesoftware.smack.packet.ExtensionElement;
  23. import org.jivesoftware.smack.packet.Message;
  24. import org.jivesoftware.smack.packet.MessageBuilder;
  25. import org.jivesoftware.smack.packet.MessageView;
  26. import org.jivesoftware.smack.util.StringUtils;
  27. import org.jivesoftware.smack.util.XmlStringBuilder;

  28. public class ExplicitMessageEncryptionElement implements ExtensionElement {

  29.     private static final Map<String, ExplicitMessageEncryptionProtocol> PROTOCOL_LUT = new HashMap<>();

  30.     public static final String ELEMENT = "encryption";

  31.     public static final String NAMESPACE = "urn:xmpp:eme:0";

  32.     public static final QName QNAME = new QName(NAMESPACE, ELEMENT);

  33.     public enum ExplicitMessageEncryptionProtocol {

  34.         /**
  35.          * The encryption method specified in <a href="https://xmpp.org/extensions/xep-0373.html">XEP-0373: OpenPGP for
  36.          * XMPP</a>.
  37.          */
  38.         openpgpV0("urn:xmpp:openpgp:0", "OpenPGP for XMPP (XEP-0373)"),

  39.         otrV0("urn:xmpp:otr:0", "Off-the-Record Messaging (XEP-0364)"),

  40.         omemoVAxolotl("eu.siacs.conversations.axolotl", "OMEMO Multi End Message and Object Encryption (XEP-0384)"),

  41.         legacyOpenPGP("jabber:x:encrypted", "Legacy OpenPGP for XMPP [DANGEROUS, DO NOT USE!]"),
  42.         ;

  43.         private final String namespace;
  44.         private final String name;

  45.         ExplicitMessageEncryptionProtocol(String namespace, String name) {
  46.             this.namespace = namespace;
  47.             this.name = name;
  48.             PROTOCOL_LUT.put(namespace, this);
  49.         }

  50.         public String getNamespace() {
  51.             return namespace;
  52.         }

  53.         public String getName() {
  54.             return name;
  55.         }

  56.         public static ExplicitMessageEncryptionProtocol from(String namespace) {
  57.             return PROTOCOL_LUT.get(namespace);
  58.         }
  59.     }

  60.     private final String encryptionNamespace;

  61.     private final String name;

  62.     private boolean isUnknownProtocol;

  63.     private ExplicitMessageEncryptionProtocol protocolCache;

  64.     public ExplicitMessageEncryptionElement(ExplicitMessageEncryptionProtocol protocol) {
  65.         this(protocol.getNamespace(), protocol.getName());
  66.     }

  67.     public ExplicitMessageEncryptionElement(String encryptionNamespace) {
  68.         this(encryptionNamespace, null);
  69.     }

  70.     public ExplicitMessageEncryptionElement(String encryptionNamespace, String name) {
  71.         this.encryptionNamespace = StringUtils.requireNotNullNorEmpty(encryptionNamespace,
  72.                         "encryptionNamespace must not be null");
  73.         this.name = name;
  74.     }

  75.     public ExplicitMessageEncryptionProtocol getProtocol() {
  76.         if (protocolCache != null) {
  77.             return protocolCache;
  78.         }

  79.         if (isUnknownProtocol) {
  80.             return null;
  81.         }

  82.         ExplicitMessageEncryptionProtocol protocol = PROTOCOL_LUT.get(encryptionNamespace);
  83.         if (protocol == null) {
  84.             isUnknownProtocol = true;
  85.             return null;
  86.         }

  87.         protocolCache = protocol;
  88.         return protocol;
  89.     }

  90.     public String getEncryptionNamespace() {
  91.         return encryptionNamespace;
  92.     }

  93.     /**
  94.      * Get the optional name of the encryption method.
  95.      *
  96.      * @return the name of the encryption method or <code>null</code>.
  97.      */
  98.     public String getName() {
  99.         return name;
  100.     }

  101.     @Override
  102.     public String getElementName() {
  103.         return ELEMENT;
  104.     }

  105.     @Override
  106.     public String getNamespace() {
  107.         return NAMESPACE;
  108.     }

  109.     @Override
  110.     public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
  111.         XmlStringBuilder xml = new XmlStringBuilder(this);
  112.         xml.attribute("namespace", getEncryptionNamespace());
  113.         xml.optAttribute("name", getName());
  114.         xml.closeEmptyElement();
  115.         return xml;
  116.     }

  117.     public static ExplicitMessageEncryptionElement from(Message message) {
  118.         return message.getExtension(ExplicitMessageEncryptionElement.class);
  119.     }

  120.     /**
  121.      * Return true, if the {@code message} already contains an EME element with the specified {@code protocolNamespace}.
  122.      *
  123.      * @param message message
  124.      * @param protocolNamespace namespace
  125.      * @return true if message has EME element for that namespace, otherwise false
  126.      */
  127.     public static boolean hasProtocol(MessageView message, String protocolNamespace) {
  128.         List<ExplicitMessageEncryptionElement> emeElements = message
  129.                 .getExtensions(ExplicitMessageEncryptionElement.class);

  130.         for (ExplicitMessageEncryptionElement emeElement : emeElements) {
  131.             if (emeElement.getEncryptionNamespace().equals(protocolNamespace)) {
  132.                 return true;
  133.             }
  134.         }

  135.         return false;
  136.     }

  137.     /**
  138.      * Return true, if the {@code message} already contains an EME element with the specified protocol namespace.
  139.      *
  140.      * @param message message
  141.      * @param protocol protocol
  142.      * @return true if message has EME element for that namespace, otherwise false
  143.      */
  144.     public static boolean hasProtocol(MessageView message, ExplicitMessageEncryptionProtocol protocol) {
  145.         return hasProtocol(message, protocol.namespace);
  146.     }

  147.     /**
  148.      * Add an EME element containing the specified {@code protocol} namespace to the message.
  149.      * In case there is already an element with that protocol, we do nothing.
  150.      *
  151.      * @param message a message builder.
  152.      * @param protocol encryption protocol
  153.      */
  154.     public static void set(MessageBuilder message, ExplicitMessageEncryptionProtocol protocol) {
  155.         if (!hasProtocol(message, protocol.namespace)) {
  156.             message.addExtension(new ExplicitMessageEncryptionElement(protocol));
  157.         }
  158.     }
  159. }