001/** 002 * 003 * Copyright 2017-2019 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.eme.element; 018 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022 023import javax.xml.namespace.QName; 024 025import org.jivesoftware.smack.packet.ExtensionElement; 026import org.jivesoftware.smack.packet.Message; 027import org.jivesoftware.smack.packet.MessageBuilder; 028import org.jivesoftware.smack.packet.MessageView; 029import org.jivesoftware.smack.util.StringUtils; 030import org.jivesoftware.smack.util.XmlStringBuilder; 031 032public class ExplicitMessageEncryptionElement implements ExtensionElement { 033 034 private static final Map<String, ExplicitMessageEncryptionProtocol> PROTOCOL_LUT = new HashMap<>(); 035 036 public static final String ELEMENT = "encryption"; 037 038 public static final String NAMESPACE = "urn:xmpp:eme:0"; 039 040 public static final QName QNAME = new QName(NAMESPACE, ELEMENT); 041 042 public enum ExplicitMessageEncryptionProtocol { 043 044 /** 045 * The encryption method specified in <a href="https://xmpp.org/extensions/xep-0373.html">XEP-0373: OpenPGP for 046 * XMPP</a>. 047 */ 048 openpgpV0("urn:xmpp:openpgp:0", "OpenPGP for XMPP (XEP-0373)"), 049 050 otrV0("urn:xmpp:otr:0", "Off-the-Record Messaging (XEP-0364)"), 051 052 omemoVAxolotl("eu.siacs.conversations.axolotl", "OMEMO Multi End Message and Object Encryption (XEP-0384)"), 053 054 legacyOpenPGP("jabber:x:encrypted", "Legacy OpenPGP for XMPP [DANGEROUS, DO NOT USE!]"), 055 ; 056 057 private final String namespace; 058 private final String name; 059 060 ExplicitMessageEncryptionProtocol(String namespace, String name) { 061 this.namespace = namespace; 062 this.name = name; 063 PROTOCOL_LUT.put(namespace, this); 064 } 065 066 public String getNamespace() { 067 return namespace; 068 } 069 070 public String getName() { 071 return name; 072 } 073 074 public static ExplicitMessageEncryptionProtocol from(String namespace) { 075 return PROTOCOL_LUT.get(namespace); 076 } 077 } 078 079 private final String encryptionNamespace; 080 081 private final String name; 082 083 private boolean isUnknownProtocol; 084 085 private ExplicitMessageEncryptionProtocol protocolCache; 086 087 public ExplicitMessageEncryptionElement(ExplicitMessageEncryptionProtocol protocol) { 088 this(protocol.getNamespace(), protocol.getName()); 089 } 090 091 public ExplicitMessageEncryptionElement(String encryptionNamespace) { 092 this(encryptionNamespace, null); 093 } 094 095 public ExplicitMessageEncryptionElement(String encryptionNamespace, String name) { 096 this.encryptionNamespace = StringUtils.requireNotNullNorEmpty(encryptionNamespace, 097 "encryptionNamespace must not be null"); 098 this.name = name; 099 } 100 101 public ExplicitMessageEncryptionProtocol getProtocol() { 102 if (protocolCache != null) { 103 return protocolCache; 104 } 105 106 if (isUnknownProtocol) { 107 return null; 108 } 109 110 ExplicitMessageEncryptionProtocol protocol = PROTOCOL_LUT.get(encryptionNamespace); 111 if (protocol == null) { 112 isUnknownProtocol = true; 113 return null; 114 } 115 116 protocolCache = protocol; 117 return protocol; 118 } 119 120 public String getEncryptionNamespace() { 121 return encryptionNamespace; 122 } 123 124 /** 125 * Get the optional name of the encryption method. 126 * 127 * @return the name of the encryption method or <code>null</code>. 128 */ 129 public String getName() { 130 return name; 131 } 132 133 @Override 134 public String getElementName() { 135 return ELEMENT; 136 } 137 138 @Override 139 public String getNamespace() { 140 return NAMESPACE; 141 } 142 143 @Override 144 public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 145 XmlStringBuilder xml = new XmlStringBuilder(this); 146 xml.attribute("namespace", getEncryptionNamespace()); 147 xml.optAttribute("name", getName()); 148 xml.closeEmptyElement(); 149 return xml; 150 } 151 152 public static ExplicitMessageEncryptionElement from(Message message) { 153 return message.getExtension(ExplicitMessageEncryptionElement.class); 154 } 155 156 /** 157 * Return true, if the {@code message} already contains an EME element with the specified {@code protocolNamespace}. 158 * 159 * @param message message 160 * @param protocolNamespace namespace 161 * @return true if message has EME element for that namespace, otherwise false 162 */ 163 public static boolean hasProtocol(MessageView message, String protocolNamespace) { 164 List<ExplicitMessageEncryptionElement> emeElements = message 165 .getExtensions(ExplicitMessageEncryptionElement.class); 166 167 for (ExplicitMessageEncryptionElement emeElement : emeElements) { 168 if (emeElement.getEncryptionNamespace().equals(protocolNamespace)) { 169 return true; 170 } 171 } 172 173 return false; 174 } 175 176 /** 177 * Return true, if the {@code message} already contains an EME element with the specified protocol namespace. 178 * 179 * @param message message 180 * @param protocol protocol 181 * @return true if message has EME element for that namespace, otherwise false 182 */ 183 public static boolean hasProtocol(MessageView message, ExplicitMessageEncryptionProtocol protocol) { 184 return hasProtocol(message, protocol.namespace); 185 } 186 187 /** 188 * Add an EME element containing the specified {@code protocol} namespace to the message. 189 * In case there is already an element with that protocol, we do nothing. 190 * 191 * @param message a message builder. 192 * @param protocol encryption protocol 193 */ 194 public static void set(MessageBuilder message, ExplicitMessageEncryptionProtocol protocol) { 195 if (!hasProtocol(message, protocol.namespace)) { 196 message.addExtension(new ExplicitMessageEncryptionElement(protocol)); 197 } 198 } 199}