diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/ExplicitMessageEncryptionManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/ExplicitMessageEncryptionManager.java new file mode 100644 index 000000000..b1808c45d --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/ExplicitMessageEncryptionManager.java @@ -0,0 +1,57 @@ +/** + * + * Copyright 2017 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.eme; + +import java.util.Map; +import java.util.WeakHashMap; + +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPConnectionRegistry; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement; + +public final class ExplicitMessageEncryptionManager { + + private static final Map INSTANCES = new WeakHashMap<>(); + + static { + XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { + @Override + public void connectionCreated(XMPPConnection connection) { + getInstanceFor(connection); + } + }); + } + + public static final String NAMESPACE_V0 = ExplicitMessageEncryptionElement.NAMESPACE; + + public static synchronized ExplicitMessageEncryptionManager getInstanceFor(XMPPConnection connection) { + ExplicitMessageEncryptionManager manager = INSTANCES.get(connection); + if (manager == null) { + manager = new ExplicitMessageEncryptionManager(connection); + INSTANCES.put(connection, manager); + } + return manager; + } + + private ExplicitMessageEncryptionManager(XMPPConnection connection) { + ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); + sdm.addFeature(NAMESPACE_V0); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/element/ExplicitMessageEncryptionElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/element/ExplicitMessageEncryptionElement.java new file mode 100644 index 000000000..c98f22cd4 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/element/ExplicitMessageEncryptionElement.java @@ -0,0 +1,146 @@ +/** + * + * Copyright 2017 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.eme.element; + +import java.util.HashMap; +import java.util.Map; + +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.util.XmlStringBuilder; + +public class ExplicitMessageEncryptionElement implements ExtensionElement { + + private static final Map PROTOCOL_LUT = new HashMap<>(); + + public static final String ELEMENT = "encryption"; + + public static final String NAMESPACE = "urn:xmpp:eme:0"; + + public enum ExplicitMessageEncryptionProtocol { + + /** + * The encryption method specified in XEP-0373: OpenPGP for + * XMPP. + */ + openpgpV0("urn:xmpp:openpgp:0", "OpenPGP for XMPP (XEP-0373)"), + + otrV0("urn:xmpp:otr:0", "Off-the-Record Messaging (XEP-0364)"), + + legacyOpenPGP("jabber:x:encrypted", "Legacy OpenPGP for XMPP [DANGEROUS, DO NOT USE!]"), + ; + + private final String namespace; + private final String name; + + private ExplicitMessageEncryptionProtocol(String namespace, String name) { + this.namespace = namespace; + this.name = name; + PROTOCOL_LUT.put(namespace, this); + } + + public String getNamespace() { + return namespace; + } + + public String getName() { + return name; + } + + public static ExplicitMessageEncryptionProtocol from(String namespace) { + return PROTOCOL_LUT.get(namespace); + } + } + + private final String encryptionNamespace; + + private final String name; + + private boolean isUnknownProtocol; + + private ExplicitMessageEncryptionProtocol protocolCache; + + public ExplicitMessageEncryptionElement(ExplicitMessageEncryptionProtocol protocol) { + this(protocol.getNamespace(), protocol.getName()); + } + + public ExplicitMessageEncryptionElement(String encryptionNamespace) { + this(encryptionNamespace, null); + } + + public ExplicitMessageEncryptionElement(String encryptionNamespace, String name) { + this.encryptionNamespace = StringUtils.requireNotNullOrEmpty(encryptionNamespace, + "encryptionNamespace must not be null"); + this.name = name; + } + + public ExplicitMessageEncryptionProtocol getProtocol() { + if (protocolCache != null) { + return protocolCache; + } + + if (isUnknownProtocol) { + return null; + } + + ExplicitMessageEncryptionProtocol protocol = PROTOCOL_LUT.get(encryptionNamespace); + if (protocol == null) { + isUnknownProtocol = true; + return null; + } + + protocolCache = protocol; + return protocol; + } + + public String getEncryptionNamespace() { + return encryptionNamespace; + } + + /** + * Get the optional name of the encryption method. + * + * @return the name of the encryption method or null. + */ + public String getName() { + return name; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public XmlStringBuilder toXML() { + XmlStringBuilder xml = new XmlStringBuilder(this); + xml.attribute("namespace", getEncryptionNamespace()); + xml.optAttribute("name", getName()); + xml.closeEmptyElement(); + return xml; + } + + public static ExplicitMessageEncryptionElement from(Message message) { + return message.getExtension(ELEMENT, NAMESPACE); + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/element/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/element/package-info.java new file mode 100644 index 000000000..9b4b286e7 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/element/package-info.java @@ -0,0 +1,20 @@ +/** + * + * Copyright 2017 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * XMPP extension elements for XEP-0380: Explicit Message Encryption. + */ +package org.jivesoftware.smackx.eme.element; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/package-info.java new file mode 100644 index 000000000..ae132d815 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/package-info.java @@ -0,0 +1,20 @@ +/** + * + * Copyright 2017 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Smack's API for XEP-0380: Explicit Message Encryption. + */ +package org.jivesoftware.smackx.eme; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/provider/ExplicitMessageEncryptionProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/provider/ExplicitMessageEncryptionProvider.java new file mode 100644 index 000000000..b466f4b70 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/provider/ExplicitMessageEncryptionProvider.java @@ -0,0 +1,32 @@ +/** + * + * Copyright 2017 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.eme.provider; + +import org.jivesoftware.smack.provider.ExtensionElementProvider; +import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement; +import org.xmlpull.v1.XmlPullParser; + +public class ExplicitMessageEncryptionProvider extends ExtensionElementProvider { + + @Override + public ExplicitMessageEncryptionElement parse(XmlPullParser parser, int initialDepth) throws Exception { + String namespace = parser.getAttributeValue(null, "namespace"); + String name = parser.getAttributeValue(null, "name"); + return new ExplicitMessageEncryptionElement(namespace, name); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/provider/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/provider/package-info.java new file mode 100644 index 000000000..611873f17 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/eme/provider/package-info.java @@ -0,0 +1,20 @@ +/** + * + * Copyright 2017 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Smack Provider for XEP-0380: Explicit Message Encryption. + */ +package org.jivesoftware.smackx.eme.provider; diff --git a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers index 2598dceea..21a8281f0 100644 --- a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers +++ b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers @@ -277,4 +277,11 @@ g org.jivesoftware.smackx.httpfileupload.provider.FileTooLargeErrorProvider + + + encryption + urn:xmpp:eme:0 + org.jivesoftware.smackx.eme.provider.ExplicitMessageEncryptionProvider + + diff --git a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.xml b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.xml index 05b819a27..ab592717a 100644 --- a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.xml +++ b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.xml @@ -5,5 +5,6 @@ org.jivesoftware.smackx.iot.discovery.IoTDiscoveryManager org.jivesoftware.smackx.iot.provisioning.IoTProvisioningManager org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager + org.jivesoftware.smackx.eme.ExplicitMessageEncryptionManager diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/eme/provider/ExplicitMessageEncryptionProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/eme/provider/ExplicitMessageEncryptionProviderTest.java new file mode 100644 index 000000000..248b9e39a --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/eme/provider/ExplicitMessageEncryptionProviderTest.java @@ -0,0 +1,47 @@ +/** + * + * Copyright 2017 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.eme.provider; + +import static org.junit.Assert.assertEquals; + +import org.jivesoftware.smack.test.util.TestUtils; +import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement; +import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol; +import org.junit.Test; + +public class ExplicitMessageEncryptionProviderTest { + + private static final String OX_EME_ELEMENT = ""; + + private static final String UNKNOWN_NAMESPACE = "urn:xmpp:foobar:0"; + private static final String UNKNOWN_NAME = "Foo Bar"; + private static final String UNKNOWN_EME_ELEMENT = ""; + + @Test + public void testParseOxEmeElement() throws Exception { + ExplicitMessageEncryptionElement eme = TestUtils.parseExtensionElement(OX_EME_ELEMENT); + assertEquals(ExplicitMessageEncryptionProtocol.openpgpV0, eme.getProtocol()); + } + + @Test + public void testParseUnknownEmeElement() throws Exception { + ExplicitMessageEncryptionElement eme = TestUtils.parseExtensionElement(UNKNOWN_EME_ELEMENT); + assertEquals(UNKNOWN_NAMESPACE, eme.getEncryptionNamespace()); + assertEquals(UNKNOWN_NAME, eme.getName()); + } +}