diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/JingleEncryptedTransferManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/JingleEncryptedTransferManager.java new file mode 100644 index 000000000..7b92026b0 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/JingleEncryptedTransferManager.java @@ -0,0 +1,48 @@ +package org.jivesoftware.smackx.jingle_encrypted_transfer; + +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.jivesoftware.smack.Manager; +import org.jivesoftware.smack.XMPPConnection; + +/** + * Created by vanitas on 13.07.17. + */ +public class JingleEncryptedTransferManager extends Manager { + + public static final String NAMESPACE = "urn:xmpp:jingle:jet:0"; + + private static final WeakHashMap INSTANCES = new WeakHashMap<>(); + + private static final Map encryptionProviders = new HashMap<>(); + + private JingleEncryptedTransferManager(XMPPConnection connection) { + super(connection); + } + + public static JingleEncryptedTransferManager getInstanceFor(XMPPConnection connection) { + JingleEncryptedTransferManager manager = INSTANCES.get(connection); + + if (manager == null) { + manager = new JingleEncryptedTransferManager(connection); + INSTANCES.put(connection, manager); + } + + return manager; + } + + + public void registerEncryptionProvider(String namespace, JingleEncryptionMethod provider) { + encryptionProviders.put(namespace, provider); + } + + public void unregisterEncryptionProvider(String namespace) { + encryptionProviders.remove(namespace); + } + + public JingleEncryptionMethod getEncryptionProvider(String namespace) { + return encryptionProviders.get(namespace); + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/JingleEncryptionMethod.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/JingleEncryptionMethod.java new file mode 100644 index 000000000..79ebfb025 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/JingleEncryptionMethod.java @@ -0,0 +1,44 @@ +/** + * + * Copyright 2017 Paul Schaub + * + * 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.jingle_encrypted_transfer; + +import java.security.NoSuchAlgorithmException; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.provider.ExtensionElementProvider; + +import org.jxmpp.jid.FullJid; + +/** + * Created by vanitas on 13.07.17. + */ +public interface JingleEncryptionMethod { + + ExtensionElement encryptJingleTransfer(FullJid recipient, byte[] keyData) throws JingleEncryptionException, InterruptedException, NoSuchAlgorithmException, SmackException.NotConnectedException, SmackException.NoResponseException; + + byte[] decryptJingleTransfer(FullJid sender, ExtensionElement encryptionElement) throws JingleEncryptionException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException; + + ExtensionElementProvider getEncryptionElementProvider(); + + class JingleEncryptionException extends Exception { + public JingleEncryptionException(Throwable throwable) { + super(throwable); + } + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/JingleEncryptionMethodManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/JingleEncryptionMethodManager.java new file mode 100644 index 000000000..98793e4ed --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/JingleEncryptionMethodManager.java @@ -0,0 +1,26 @@ +package org.jivesoftware.smackx.jingle_encrypted_transfer; + +import java.util.HashMap; + +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.provider.ExtensionElementProvider; + +/** + * Created by vanitas on 13.07.17. + */ +public class JingleEncryptionMethodManager { + + public static HashMap> securityKeyTransportProviders = new HashMap<>(); + + private JingleEncryptionMethodManager() { + // $(man true) + } + + public static void registerSecurityKeyTransportProvider(String namespace, ExtensionElementProvider provider) { + securityKeyTransportProviders.put(namespace, provider); + } + + public static ExtensionElementProvider getSecurityKeyTransportProvider(String namespace) { + return securityKeyTransportProviders.get(namespace); + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/element/SecurityElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/element/SecurityElement.java new file mode 100644 index 000000000..4f927af5b --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/element/SecurityElement.java @@ -0,0 +1,42 @@ +package org.jivesoftware.smackx.jingle_encrypted_transfer.element; + +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smackx.jingle_encrypted_transfer.JingleEncryptedTransferManager; + +/** + * Created by vanitas on 13.07.17. + */ +public class SecurityElement implements ExtensionElement { + public static final String ELEMENT = "security"; + public static final String ATTR_NAME = "name"; + public static final String ATTR_TYPE = "type"; + + private final ExtensionElement child; + private final String name; + + public SecurityElement(String name, ExtensionElement child) { + this.name = name; + this.child = child; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(this); + xml.attribute(ATTR_NAME, name).attribute(ATTR_TYPE, child.getNamespace()); + xml.rightAngleBracket(); + xml.element(child); + xml.closeElement(this); + return xml; + } + + @Override + public String getNamespace() { + return JingleEncryptedTransferManager.NAMESPACE; + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/provider/SecurityProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/provider/SecurityProvider.java new file mode 100644 index 000000000..502c2c9e5 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_encrypted_transfer/provider/SecurityProvider.java @@ -0,0 +1,41 @@ +package org.jivesoftware.smackx.jingle_encrypted_transfer.provider; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.provider.ExtensionElementProvider; +import org.jivesoftware.smack.util.Objects; +import org.jivesoftware.smackx.jingle_encrypted_transfer.JingleEncryptedTransferManager; +import org.jivesoftware.smackx.jingle_encrypted_transfer.JingleEncryptionMethodManager; +import org.jivesoftware.smackx.jingle_encrypted_transfer.element.SecurityElement; + +import org.xmlpull.v1.XmlPullParser; + +/** + * Created by vanitas on 13.07.17. + */ +public class SecurityProvider extends ExtensionElementProvider { + private static final Logger LOGGER = Logger.getLogger(SecurityProvider.class.getName()); + + @Override + public SecurityElement parse(XmlPullParser parser, int initialDepth) throws Exception { + String name = parser.getAttributeValue(JingleEncryptedTransferManager.NAMESPACE, SecurityElement.ATTR_NAME); + String type = parser.getAttributeValue(JingleEncryptedTransferManager.NAMESPACE, SecurityElement.ATTR_TYPE); + ExtensionElement child; + + Objects.requireNonNull(type); + + ExtensionElementProvider encryptionElementProvider = + JingleEncryptionMethodManager.getSecurityKeyTransportProvider(type); + + if (encryptionElementProvider != null) { + child = encryptionElementProvider.parse(parser); + } else { + LOGGER.log(Level.WARNING, "Unknown child element in SecurityElement: " + type); + return null; + } + + return new SecurityElement(name, child); + } +} diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java index dbebf20ce..066f7e494 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java @@ -42,10 +42,12 @@ import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.util.stringencoder.Base64; import org.jivesoftware.smackx.carbons.CarbonManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement; import org.jivesoftware.smackx.hints.element.StoreHint; +import org.jivesoftware.smackx.jingle_encrypted_transfer.JingleEncryptionMethod; import org.jivesoftware.smackx.mam.MamManager; import org.jivesoftware.smackx.muc.MultiUserChat; import org.jivesoftware.smackx.muc.MultiUserChatManager; @@ -89,7 +91,7 @@ import org.jxmpp.stringprep.XmppStringprepException; * @author Paul Schaub */ -public final class OmemoManager extends Manager { +public final class OmemoManager extends Manager implements JingleEncryptionMethod { private static final Logger LOGGER = Logger.getLogger(OmemoManager.class.getName()); private static final WeakHashMap> INSTANCES = new WeakHashMap<>(); @@ -886,4 +888,44 @@ public final class OmemoManager extends Manager { } return omemoCarbonCopyListener; } + + @Override + public ExtensionElement encryptJingleTransfer(FullJid recipient, byte[] keyData) throws JingleEncryptionException, InterruptedException, NoSuchAlgorithmException, SmackException.NotConnectedException, SmackException.NoResponseException { + BareJid bareJid = recipient.asBareJid(); + Message EncryptedMessage; + try { + EncryptedMessage = encrypt(bareJid, Base64.encodeToString(keyData)); + } catch (CryptoFailedException | UndecidedOmemoIdentityException | CannotEstablishOmemoSessionException e) { + throw new JingleEncryptionException(e); + } + + ExtensionElement encryptionElement = EncryptedMessage.getExtension(OmemoElement.ENCRYPTED, OMEMO_NAMESPACE_V_AXOLOTL); + if (encryptionElement == null) { + throw new AssertionError("OmemoElement MUST NOT be null."); + } + + return encryptionElement; + } + + @Override + public byte[] decryptJingleTransfer(FullJid sender, ExtensionElement encryptionElement) throws JingleEncryptionException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException { + if (!encryptionElement.getNamespace().equals(OMEMO_NAMESPACE_V_AXOLOTL) + || !encryptionElement.getElementName().equals(OmemoElement.ENCRYPTED)) { + throw new IllegalArgumentException("Passed ExtensionElement MUST be an OmemoElement!"); + } + + OmemoElement omemoElement = (OmemoElement) encryptionElement; + Message pseudoMessage = new Message(); + pseudoMessage.setFrom(sender.asBareJid()); + pseudoMessage.addExtension(omemoElement); + + ClearTextMessage decryptedPseudoMessage; + try { + decryptedPseudoMessage = decrypt(sender.asBareJid(), pseudoMessage); + } catch (CryptoFailedException | CorruptedOmemoKeyException | NoRawSessionException e) { + throw new JingleEncryptionException(e); + } + + return Base64.decode(decryptedPseudoMessage.getBody()); + } }