mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-23 20:42:06 +01:00
Add support for XEP-xxxx Jingle Encrypted Transports
This commit is contained in:
parent
a53a9f4f76
commit
c77aee7010
24 changed files with 1382 additions and 11 deletions
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
*
|
||||
* 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.ciphers;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
public class Aes128GcmNoPadding extends AesGcmNoPadding {
|
||||
public static final String NAMESPACE = "urn:xmpp:ciphers:aes-128-gcm-nopadding:0";
|
||||
|
||||
public Aes128GcmNoPadding(int MODE) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
|
||||
NoSuchProviderException, InvalidAlgorithmParameterException {
|
||||
super(128, MODE);
|
||||
}
|
||||
|
||||
public Aes128GcmNoPadding(byte[] keyAndIv, int MODE) throws NoSuchProviderException, InvalidAlgorithmParameterException,
|
||||
NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException {
|
||||
super(AesGcmNoPadding.copyOfRange(keyAndIv, 0, 16), // 16 byte key
|
||||
AesGcmNoPadding.copyOfRange(keyAndIv, 16, keyAndIv.length), MODE); // rest (12 byte) IV
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
*
|
||||
* 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.ciphers;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
public class Aes256GcmNoPadding extends AesGcmNoPadding {
|
||||
public static final String NAMESPACE = "urn:xmpp:ciphers:aes-256-gcm-nopadding:0";
|
||||
|
||||
public Aes256GcmNoPadding(int MODE) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
|
||||
NoSuchProviderException, InvalidAlgorithmParameterException {
|
||||
super(256, MODE);
|
||||
}
|
||||
|
||||
public Aes256GcmNoPadding(byte[] keyAndIv, int MODE) throws NoSuchProviderException, InvalidAlgorithmParameterException,
|
||||
NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException {
|
||||
super(AesGcmNoPadding.copyOfRange(keyAndIv, 0, 32), // 32 byte key
|
||||
AesGcmNoPadding.copyOfRange(keyAndIv, 32, keyAndIv.length), MODE); // rest (12 byte) IV
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
*
|
||||
* 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.ciphers;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public abstract class AesGcmNoPadding {
|
||||
|
||||
public static final String keyType = "AES";
|
||||
public static final String cipherMode = "AES/GCM/NoPadding";
|
||||
|
||||
private final int length;
|
||||
protected final Cipher cipher;
|
||||
private final byte[] key, iv, keyAndIv;
|
||||
|
||||
AesGcmNoPadding(int bits, int MODE) throws NoSuchAlgorithmException, NoSuchProviderException,
|
||||
NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
|
||||
this.length = bits;
|
||||
int bytes = bits / 8;
|
||||
|
||||
KeyGenerator keyGenerator = KeyGenerator.getInstance(keyType);
|
||||
keyGenerator.init(bits);
|
||||
key = keyGenerator.generateKey().getEncoded();
|
||||
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
iv = new byte[12];
|
||||
secureRandom.nextBytes(iv);
|
||||
|
||||
keyAndIv = new byte[bytes + 12];
|
||||
System.arraycopy(key, 0, keyAndIv, 0, bytes);
|
||||
System.arraycopy(iv, 0, keyAndIv, bytes, 12);
|
||||
|
||||
cipher = Cipher.getInstance(cipherMode, "BC");
|
||||
SecretKey keySpec = new SecretKeySpec(key, keyType);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||
cipher.init(MODE, keySpec, ivSpec);
|
||||
}
|
||||
|
||||
public static AesGcmNoPadding createEncryptionKey(String cipherName)
|
||||
throws NoSuchProviderException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
|
||||
InvalidAlgorithmParameterException {
|
||||
|
||||
switch (cipherName) {
|
||||
case Aes128GcmNoPadding.NAMESPACE:
|
||||
return new Aes128GcmNoPadding(Cipher.ENCRYPT_MODE);
|
||||
case Aes256GcmNoPadding.NAMESPACE:
|
||||
return new Aes256GcmNoPadding(Cipher.ENCRYPT_MODE);
|
||||
default: throw new NoSuchAlgorithmException("Invalid cipher.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AES key.
|
||||
* @param key key
|
||||
* @param iv iv
|
||||
* @param MODE cipher mode (Cipher.ENCRYPT_MODE / Cipher.DECRYPT_MODE)
|
||||
* @throws NoSuchPaddingException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws NoSuchProviderException
|
||||
* @throws InvalidAlgorithmParameterException
|
||||
* @throws InvalidKeyException
|
||||
*/
|
||||
public AesGcmNoPadding(byte[] key, byte[] iv, int MODE) throws NoSuchPaddingException, NoSuchAlgorithmException,
|
||||
NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException {
|
||||
assert iv.length == 12;
|
||||
this.length = key.length * 8;
|
||||
this.key = key;
|
||||
this.iv = iv;
|
||||
|
||||
keyAndIv = new byte[key.length + iv.length];
|
||||
System.arraycopy(key, 0, keyAndIv, 0, key.length);
|
||||
System.arraycopy(iv, 0, keyAndIv, key.length, iv.length);
|
||||
|
||||
cipher = Cipher.getInstance(cipherMode, "BC");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(key, keyType);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||
cipher.init(MODE, keySpec, ivSpec);
|
||||
}
|
||||
|
||||
public static AesGcmNoPadding createDecryptionKey(String namespace, byte[] serialized)
|
||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException,
|
||||
InvalidKeyException, NoSuchPaddingException {
|
||||
|
||||
switch (namespace) {
|
||||
case Aes128GcmNoPadding.NAMESPACE:
|
||||
return new Aes128GcmNoPadding(serialized, Cipher.DECRYPT_MODE);
|
||||
case Aes256GcmNoPadding.NAMESPACE:
|
||||
return new Aes256GcmNoPadding(serialized, Cipher.DECRYPT_MODE);
|
||||
default: throw new NoSuchAlgorithmException("Invalid cipher.");
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getKeyAndIv() {
|
||||
return keyAndIv.clone();
|
||||
}
|
||||
|
||||
public byte[] getKey() {
|
||||
return key.clone();
|
||||
}
|
||||
|
||||
public byte[] getIv() {
|
||||
return iv.clone();
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public Cipher getCipher() {
|
||||
return cipher;
|
||||
}
|
||||
|
||||
public abstract String getNamespace();
|
||||
|
||||
static byte[] copyOfRange(byte[] source, int start, int end) {
|
||||
byte[] copy = new byte[end - start];
|
||||
System.arraycopy(source, start, copy, 0, end - start);
|
||||
return copy;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Smack's API for XEP-XXXX: Ciphers</a>.
|
||||
* This contains some AES cipher utility functions.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ciphers;
|
|
@ -0,0 +1,187 @@
|
|||
/**
|
||||
*
|
||||
* 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.jet;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.util.HashMap;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import org.jivesoftware.smack.Manager;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||
import org.jivesoftware.smackx.ciphers.Aes256GcmNoPadding;
|
||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||
import org.jivesoftware.smackx.jet.component.JetSecurity;
|
||||
import org.jivesoftware.smackx.jet.provider.JetSecurityProvider;
|
||||
import org.jivesoftware.smackx.jingle.JingleDescriptionManager;
|
||||
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||
import org.jivesoftware.smackx.jingle.JingleTransportManager;
|
||||
import org.jivesoftware.smackx.jingle.component.JingleContent;
|
||||
import org.jivesoftware.smackx.jingle.component.JingleSession;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||
import org.jivesoftware.smackx.jingle.util.Role;
|
||||
import org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferManager;
|
||||
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFile;
|
||||
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleOutgoingFileOffer;
|
||||
import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileOfferController;
|
||||
|
||||
import org.jxmpp.jid.FullJid;
|
||||
|
||||
/**
|
||||
* Manager for Jingle Encrypted Transfers (XEP-XXXX).
|
||||
*/
|
||||
public final class JetManager extends Manager implements JingleDescriptionManager {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(JetManager.class.getName());
|
||||
|
||||
private static final WeakHashMap<XMPPConnection, JetManager> INSTANCES = new WeakHashMap<>();
|
||||
private static final HashMap<String, JingleEnvelopeManager> envelopeManagers = new HashMap<>();
|
||||
private static final HashMap<String, ExtensionElementProvider<?>> envelopeProviders = new HashMap<>();
|
||||
|
||||
private final JingleManager jingleManager;
|
||||
|
||||
static {
|
||||
JingleManager.addJingleSecurityAdapter(new JetSecurityAdapter());
|
||||
JingleManager.addJingleSecurityProvider(new JetSecurityProvider());
|
||||
}
|
||||
|
||||
private JetManager(XMPPConnection connection) {
|
||||
super(connection);
|
||||
this.jingleManager = JingleManager.getInstanceFor(connection);
|
||||
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(getNamespace());
|
||||
jingleManager.addJingleDescriptionManager(this);
|
||||
}
|
||||
|
||||
public static JetManager getInstanceFor(XMPPConnection connection) {
|
||||
JetManager manager = INSTANCES.get(connection);
|
||||
|
||||
if (manager == null) {
|
||||
manager = new JetManager(connection);
|
||||
INSTANCES.put(connection, manager);
|
||||
}
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
public OutgoingFileOfferController sendEncryptedFile(File file, FullJid recipient, JingleEnvelopeManager envelopeManager) throws Exception {
|
||||
return sendEncryptedFile(file, JingleFile.fromFile(file, null, null, null), recipient, envelopeManager);
|
||||
}
|
||||
|
||||
public OutgoingFileOfferController sendEncryptedFile(File file, JingleFile metadata, FullJid recipient, JingleEnvelopeManager envelopeManager) throws Exception {
|
||||
if (file == null || !file.exists()) {
|
||||
throw new IllegalArgumentException("File MUST NOT be null and MUST exist.");
|
||||
}
|
||||
|
||||
throwIfRecipientLacksSupport(recipient);
|
||||
|
||||
JingleSession session = jingleManager.createSession(Role.initiator, recipient);
|
||||
|
||||
JingleContent content = new JingleContent(JingleContentElement.Creator.initiator, JingleContentElement.Senders.initiator);
|
||||
session.addContent(content);
|
||||
|
||||
JingleOutgoingFileOffer offer = new JingleOutgoingFileOffer(file, metadata);
|
||||
content.setDescription(offer);
|
||||
|
||||
JingleTransportManager transportManager = jingleManager.getBestAvailableTransportManager(recipient);
|
||||
content.setTransport(transportManager.createTransportForInitiator(content));
|
||||
|
||||
JetSecurity security = new JetSecurity(envelopeManager, recipient, content.getName(), Aes256GcmNoPadding.NAMESPACE);
|
||||
content.setSecurity(security);
|
||||
session.sendInitiate(connection());
|
||||
|
||||
return offer;
|
||||
}
|
||||
|
||||
public OutgoingFileOfferController sendEncryptedStream(InputStream inputStream, JingleFile metadata, FullJid recipient, JingleEnvelopeManager envelopeManager)
|
||||
throws XMPPException.XMPPErrorException, SmackException.FeatureNotSupportedException, SmackException.NotConnectedException,
|
||||
InterruptedException, SmackException.NoResponseException, NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException,
|
||||
JingleEnvelopeManager.JingleEncryptionException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
||||
|
||||
throwIfRecipientLacksSupport(recipient);
|
||||
JingleSession session = jingleManager.createSession(Role.initiator, recipient);
|
||||
|
||||
JingleContent content = new JingleContent(JingleContentElement.Creator.initiator, JingleContentElement.Senders.initiator);
|
||||
session.addContent(content);
|
||||
|
||||
JingleOutgoingFileOffer offer = new JingleOutgoingFileOffer(inputStream, metadata);
|
||||
content.setDescription(offer);
|
||||
|
||||
JingleTransportManager transportManager = jingleManager.getBestAvailableTransportManager(recipient);
|
||||
content.setTransport(transportManager.createTransportForInitiator(content));
|
||||
|
||||
JetSecurity security = new JetSecurity(envelopeManager, recipient, content.getName(), Aes256GcmNoPadding.NAMESPACE);
|
||||
content.setSecurity(security);
|
||||
session.sendInitiate(connection());
|
||||
|
||||
return offer;
|
||||
}
|
||||
|
||||
public void registerEnvelopeManager(JingleEnvelopeManager method) {
|
||||
envelopeManagers.put(method.getJingleEnvelopeNamespace(), method);
|
||||
}
|
||||
|
||||
public void unregisterEnvelopeManager(String namespace) {
|
||||
envelopeManagers.remove(namespace);
|
||||
}
|
||||
|
||||
public JingleEnvelopeManager getEnvelopeManager(String namespace) {
|
||||
return envelopeManagers.get(namespace);
|
||||
}
|
||||
|
||||
public static void registerEnvelopeProvider(String namespace, ExtensionElementProvider<?> provider) {
|
||||
envelopeProviders.put(namespace, provider);
|
||||
}
|
||||
|
||||
public static void unregisterEnvelopeProvider(String namespace) {
|
||||
envelopeProviders.remove(namespace);
|
||||
}
|
||||
|
||||
public static ExtensionElementProvider<?> getEnvelopeProvider(String namespace) {
|
||||
return envelopeProviders.get(namespace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return JetSecurity.NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifySessionInitiate(JingleSession session) {
|
||||
JingleFileTransferManager.getInstanceFor(connection()).notifySessionInitiate(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyContentAdd(JingleSession session, JingleContent content) {
|
||||
JingleFileTransferManager.getInstanceFor(connection()).notifyContentAdd(session, content);
|
||||
}
|
||||
|
||||
private void throwIfRecipientLacksSupport(FullJid recipient) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, SmackException.FeatureNotSupportedException {
|
||||
if (!ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(recipient, getNamespace())) {
|
||||
throw new SmackException.FeatureNotSupportedException(getNamespace(), recipient);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
*
|
||||
* 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.jet;
|
||||
|
||||
import org.jivesoftware.smackx.jet.component.JetSecurity;
|
||||
import org.jivesoftware.smackx.jet.element.JetSecurityElement;
|
||||
import org.jivesoftware.smackx.jingle.adapter.JingleSecurityAdapter;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleContentSecurityElement;
|
||||
|
||||
public class JetSecurityAdapter implements JingleSecurityAdapter<JetSecurity> {
|
||||
|
||||
@Override
|
||||
public JetSecurity securityFromElement(JingleContentSecurityElement element) {
|
||||
return new JetSecurity((JetSecurityElement) element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return JetSecurity.NAMESPACE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
*
|
||||
* 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.jet;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
|
||||
import org.jxmpp.jid.FullJid;
|
||||
|
||||
/**
|
||||
* Classes that implement this interface can be used to encrypt Jingle File Transfers.
|
||||
*/
|
||||
public interface JingleEnvelopeManager {
|
||||
|
||||
ExtensionElement encryptJingleTransfer(FullJid recipient, byte[] keyData)
|
||||
throws JingleEncryptionException, InterruptedException, NoSuchAlgorithmException,
|
||||
SmackException.NotConnectedException, SmackException.NoResponseException;
|
||||
|
||||
byte[] decryptJingleTransfer(FullJid sender, ExtensionElement envelope)
|
||||
throws JingleEncryptionException, InterruptedException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, SmackException.NoResponseException;
|
||||
|
||||
class JingleEncryptionException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public JingleEncryptionException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
XMPPConnection getConnection();
|
||||
|
||||
String getJingleEnvelopeNamespace();
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
/**
|
||||
*
|
||||
* 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.jet.component;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||
import org.jivesoftware.smackx.ciphers.AesGcmNoPadding;
|
||||
import org.jivesoftware.smackx.jet.JetManager;
|
||||
import org.jivesoftware.smackx.jet.JingleEnvelopeManager;
|
||||
import org.jivesoftware.smackx.jet.element.JetSecurityElement;
|
||||
import org.jivesoftware.smackx.jingle.callbacks.JingleSecurityCallback;
|
||||
import org.jivesoftware.smackx.jingle.component.JingleSecurity;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleContentSecurityInfoElement;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||
|
||||
import org.jxmpp.jid.FullJid;
|
||||
|
||||
/**
|
||||
* Created by vanitas on 22.07.17.
|
||||
*/
|
||||
public class JetSecurity extends JingleSecurity<JetSecurityElement> {
|
||||
private static final Logger LOGGER = Logger.getLogger(JetSecurity.class.getName());
|
||||
|
||||
public static final String NAMESPACE_V0 = "urn:xmpp:jingle:jet:0";
|
||||
public static final String NAMESPACE = NAMESPACE_V0;
|
||||
|
||||
private final String envelopeNamespace;
|
||||
|
||||
private AesGcmNoPadding aesKey;
|
||||
private final ExtensionElement child;
|
||||
private final String cipherName;
|
||||
private final String contentName;
|
||||
|
||||
public JetSecurity(JetSecurityElement element) {
|
||||
super();
|
||||
this.child = element.getChild();
|
||||
this.envelopeNamespace = element.getEnvelopeNamespace();
|
||||
this.contentName = element.getContentName();
|
||||
this.cipherName = element.getCipherName();
|
||||
}
|
||||
|
||||
public JetSecurity(JingleEnvelopeManager envelopeManager, FullJid recipient, String contentName, String cipherName)
|
||||
throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
|
||||
InvalidAlgorithmParameterException, InvalidKeyException, InterruptedException,
|
||||
JingleEnvelopeManager.JingleEncryptionException, SmackException.NotConnectedException,
|
||||
SmackException.NoResponseException {
|
||||
|
||||
this.envelopeNamespace = envelopeManager.getJingleEnvelopeNamespace();
|
||||
this.aesKey = AesGcmNoPadding.createEncryptionKey(cipherName);
|
||||
this.child = envelopeManager.encryptJingleTransfer(recipient, aesKey.getKeyAndIv());
|
||||
this.contentName = contentName;
|
||||
this.cipherName = cipherName;
|
||||
}
|
||||
|
||||
private void decryptEncryptionKey(JingleEnvelopeManager method, FullJid sender)
|
||||
throws InterruptedException, JingleEnvelopeManager.JingleEncryptionException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, SmackException.NoResponseException, NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException, NoSuchProviderException, InvalidKeyException, NoSuchPaddingException {
|
||||
byte[] keyAndIv = method.decryptJingleTransfer(sender, child);
|
||||
aesKey = AesGcmNoPadding.createDecryptionKey(cipherName, keyAndIv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JetSecurityElement getElement() {
|
||||
return new JetSecurityElement(contentName, cipherName, child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JingleElement handleSecurityInfo(JingleContentSecurityInfoElement element, JingleElement wrapping) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decryptIncomingBytestream(BytestreamSession bytestreamSession, JingleSecurityCallback callback) {
|
||||
if (aesKey == null) {
|
||||
throw new IllegalStateException("Encryption key has not yet been decrypted.");
|
||||
}
|
||||
JetSecurityBytestreamSession securityBytestreamSession = new JetSecurityBytestreamSession(bytestreamSession, aesKey.getCipher());
|
||||
callback.onSecurityReady(securityBytestreamSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encryptOutgoingBytestream(BytestreamSession bytestreamSession, JingleSecurityCallback callback) {
|
||||
JetSecurityBytestreamSession securityBytestreamSession = new JetSecurityBytestreamSession(bytestreamSession, aesKey.getCipher());
|
||||
callback.onSecurityReady(securityBytestreamSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(XMPPConnection connection, FullJid sender) {
|
||||
if (getParent().getParent().isInitiator()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aesKey != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
JingleEnvelopeManager method = JetManager.getInstanceFor(connection).getEnvelopeManager(getEnvelopeNamespace());
|
||||
if (method == null) {
|
||||
throw new AssertionError("No JingleEncryptionMethodManager found for " + getEnvelopeNamespace());
|
||||
}
|
||||
try {
|
||||
decryptEncryptionKey(method, sender);
|
||||
} catch (InterruptedException | NoSuchPaddingException | InvalidKeyException | NoSuchProviderException | InvalidAlgorithmParameterException | NoSuchAlgorithmException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException | JingleEnvelopeManager.JingleEncryptionException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not decrypt security key: " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getEnvelopeNamespace() {
|
||||
return envelopeNamespace;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
*
|
||||
* 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.jet.component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.CipherOutputStream;
|
||||
|
||||
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||
import org.jivesoftware.smackx.jingle.component.JingleSecurityBytestreamSession;
|
||||
|
||||
public class JetSecurityBytestreamSession extends JingleSecurityBytestreamSession {
|
||||
private final Cipher cipher;
|
||||
|
||||
public JetSecurityBytestreamSession(BytestreamSession session, Cipher cipher) {
|
||||
super(session);
|
||||
this.cipher = cipher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new CipherInputStream(wrapped.getInputStream(), cipher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
return new CipherOutputStream(wrapped.getOutputStream(), cipher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
wrapped.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Smack's API for XEP-XXXX: Jingle Encrypted Transfers</a>.
|
||||
* Internal classes.
|
||||
*/
|
||||
package org.jivesoftware.smackx.jet.component;
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
*
|
||||
* 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.jet.element;
|
||||
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
import org.jivesoftware.smackx.jet.component.JetSecurity;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleContentSecurityElement;
|
||||
|
||||
/**
|
||||
* Implementation of the Jingle security element as specified in XEP-XXXX (Jingle Encrypted Transfers).
|
||||
* <jingle>
|
||||
* <content>
|
||||
* <description/>
|
||||
* <transport/>
|
||||
* <security/> <- You are here.
|
||||
* </content>
|
||||
* </jingle>
|
||||
*/
|
||||
public class JetSecurityElement extends JingleContentSecurityElement {
|
||||
public static final String ATTR_CONTENT_NAME = "name";
|
||||
public static final String ATTR_ENVELOPE_TYPE = "type";
|
||||
public static final String ATTR_CIPHER_TYPE = "cipher";
|
||||
|
||||
private final ExtensionElement child;
|
||||
private final String contentName;
|
||||
private final String cipherName;
|
||||
|
||||
public JetSecurityElement(String contentName, String cipherName, ExtensionElement child) {
|
||||
this.contentName = contentName;
|
||||
this.child = child;
|
||||
this.cipherName = cipherName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence toXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||
xml.attribute(ATTR_CONTENT_NAME, contentName)
|
||||
.attribute(ATTR_CIPHER_TYPE, cipherName)
|
||||
.attribute(ATTR_ENVELOPE_TYPE, child.getNamespace());
|
||||
xml.rightAngleBracket();
|
||||
xml.element(child);
|
||||
xml.closeElement(this);
|
||||
return xml;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return JetSecurity.NAMESPACE;
|
||||
}
|
||||
|
||||
public String getEnvelopeNamespace() {
|
||||
return child.getNamespace();
|
||||
}
|
||||
|
||||
public ExtensionElement getChild() {
|
||||
return child;
|
||||
}
|
||||
|
||||
public String getContentName() {
|
||||
return contentName;
|
||||
}
|
||||
|
||||
public String getCipherName() {
|
||||
return cipherName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Smack's API for XEP-XXXX: Jingle Encrypted Transfers</a>.
|
||||
* Elements.
|
||||
*/
|
||||
package org.jivesoftware.smackx.jet.element;
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Smack's API for XEP-XXXX: Jingle Encrypted Transfers</a>.
|
||||
*/
|
||||
package org.jivesoftware.smackx.jet;
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
*
|
||||
* 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.jet.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.jet.JetManager;
|
||||
import org.jivesoftware.smackx.jet.component.JetSecurity;
|
||||
import org.jivesoftware.smackx.jet.element.JetSecurityElement;
|
||||
import org.jivesoftware.smackx.jingle.provider.JingleContentSecurityProvider;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Provider for the Jingle security element for XEP-XXXX (Jingle Encrypted Transfers).
|
||||
*/
|
||||
public class JetSecurityProvider extends JingleContentSecurityProvider<JetSecurityElement> {
|
||||
private static final Logger LOGGER = Logger.getLogger(JetSecurityProvider.class.getName());
|
||||
|
||||
@Override
|
||||
public JetSecurityElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||
String name = parser.getAttributeValue("", JetSecurityElement.ATTR_CONTENT_NAME);
|
||||
String cipher = parser.getAttributeValue("", JetSecurityElement.ATTR_CIPHER_TYPE);
|
||||
String type = parser.getAttributeValue("", JetSecurityElement.ATTR_ENVELOPE_TYPE);
|
||||
ExtensionElement child;
|
||||
|
||||
Objects.requireNonNull(type);
|
||||
Objects.requireNonNull(cipher);
|
||||
|
||||
ExtensionElementProvider<?> encryptionElementProvider =
|
||||
JetManager.getEnvelopeProvider(type);
|
||||
|
||||
if (encryptionElementProvider != null) {
|
||||
child = encryptionElementProvider.parse(parser);
|
||||
} else {
|
||||
LOGGER.log(Level.WARNING, "Unknown child element in JetSecurityElement: " + type);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new JetSecurityElement(name, cipher, child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return JetSecurity.NAMESPACE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Smack's API for XEP-XXXX: Jingle Encrypted Transfers</a>.
|
||||
* Providers.
|
||||
*/
|
||||
package org.jivesoftware.smackx.jet.provider;
|
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
*
|
||||
* 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.ciphers;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class AesGcmNoPaddingTest extends SmackTestSuite {
|
||||
|
||||
@Test
|
||||
public void Aes128Test() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
AesGcmNoPadding aes128 = AesGcmNoPadding.createEncryptionKey(Aes128GcmNoPadding.NAMESPACE);
|
||||
assertNotNull(aes128);
|
||||
assertEquals(16, aes128.getKey().length);
|
||||
assertEquals(12, aes128.getIv().length);
|
||||
assertEquals(28, aes128.getKeyAndIv().length);
|
||||
assertNotNull(aes128.getCipher());
|
||||
assertEquals(128, aes128.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void Aes256Test() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
AesGcmNoPadding aes256 = AesGcmNoPadding.createEncryptionKey(Aes256GcmNoPadding.NAMESPACE);
|
||||
assertNotNull(aes256);
|
||||
assertEquals(32, aes256.getKey().length);
|
||||
assertEquals(12, aes256.getIv().length);
|
||||
assertEquals(44, aes256.getKeyAndIv().length);
|
||||
assertNotNull(aes256.getCipher());
|
||||
assertEquals(256, aes256.getLength());
|
||||
}
|
||||
|
||||
@Test(expected = NoSuchAlgorithmException.class)
|
||||
public void invalidEncryptionCipher() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
AesGcmNoPadding.createEncryptionKey("invalid");
|
||||
}
|
||||
|
||||
@Test(expected = NoSuchAlgorithmException.class)
|
||||
public void invalidDecryptionCipher() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
AesGcmNoPadding.createDecryptionKey("invalid", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encryption128Test() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidKeyException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
|
||||
AesGcmNoPadding aes1 = AesGcmNoPadding.createEncryptionKey(Aes128GcmNoPadding.NAMESPACE);
|
||||
AesGcmNoPadding aes2 = AesGcmNoPadding.createDecryptionKey(Aes128GcmNoPadding.NAMESPACE, aes1.getKeyAndIv());
|
||||
|
||||
byte[] data = new byte[4096];
|
||||
new Random().nextBytes(data);
|
||||
|
||||
byte[] enc = aes1.getCipher().doFinal(data);
|
||||
assertFalse(Arrays.equals(data, enc));
|
||||
|
||||
byte[] dec = aes2.getCipher().doFinal(enc);
|
||||
assertTrue(Arrays.equals(dec, data));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encryption256Test() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidKeyException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
|
||||
AesGcmNoPadding aes1 = AesGcmNoPadding.createEncryptionKey(Aes256GcmNoPadding.NAMESPACE);
|
||||
AesGcmNoPadding aes2 = AesGcmNoPadding.createDecryptionKey(Aes256GcmNoPadding.NAMESPACE, aes1.getKeyAndIv());
|
||||
|
||||
byte[] data = new byte[4096];
|
||||
new Random().nextBytes(data);
|
||||
|
||||
byte[] enc = aes1.getCipher().doFinal(data);
|
||||
assertFalse(Arrays.equals(data, enc));
|
||||
|
||||
byte[] dec = aes2.getCipher().doFinal(enc);
|
||||
assertTrue(Arrays.equals(dec, data));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
*
|
||||
* 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.jet;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||
import org.jivesoftware.smackx.ciphers.Aes128GcmNoPadding;
|
||||
import org.jivesoftware.smackx.jet.component.JetSecurity;
|
||||
import org.jivesoftware.smackx.jet.element.JetSecurityElement;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.jxmpp.jid.FullJid;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public class JetElementTest extends SmackTestSuite {
|
||||
|
||||
@Test
|
||||
public void jetTest() throws InterruptedException, JingleEnvelopeManager.JingleEncryptionException, NoSuchAlgorithmException, SmackException.NotConnectedException, SmackException.NoResponseException, IOException, SAXException {
|
||||
ExtensionElement child = new SecurityStub().encryptJingleTransfer(null, null);
|
||||
JetSecurityElement element = new JetSecurityElement("content1", Aes128GcmNoPadding.NAMESPACE, child);
|
||||
JetSecurity security = new JetSecurity(element);
|
||||
assertEquals(SecurityStub.NAMESPACE, security.getEnvelopeNamespace());
|
||||
assertEquals(Aes128GcmNoPadding.NAMESPACE, element.getCipherName());
|
||||
assertEquals(SecurityStub.NAMESPACE, element.getEnvelopeNamespace());
|
||||
assertEquals("content1", element.getContentName());
|
||||
|
||||
String xml = "<security xmlns='" + JetSecurity.NAMESPACE + "' " +
|
||||
"name='content1' " +
|
||||
"cipher='" + Aes128GcmNoPadding.NAMESPACE + "' " +
|
||||
"type='" + SecurityStub.NAMESPACE + "'>" +
|
||||
"<security-stub/>" +
|
||||
"</security>";
|
||||
assertXMLEqual(xml, security.getElement().toXML().toString());
|
||||
}
|
||||
|
||||
private static class SecurityStub implements JingleEnvelopeManager {
|
||||
public static final String NAMESPACE = "urn:xmpp:security-stub";
|
||||
|
||||
@Override
|
||||
public ExtensionElement encryptJingleTransfer(FullJid recipient, byte[] keyData) throws JingleEncryptionException, InterruptedException, NoSuchAlgorithmException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||
return new ExtensionElement() {
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return "security-stub";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence toXML() {
|
||||
return "<security-stub/>";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decryptJingleTransfer(FullJid sender, ExtensionElement envelope) throws JingleEncryptionException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMPPConnection getConnection() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJingleEnvelopeNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
*
|
||||
* 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.jet;
|
||||
|
||||
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.cleanServerSideTraces;
|
||||
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.setUpOmemoManager;
|
||||
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.subscribe;
|
||||
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.unidirectionalTrust;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Random;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransportManager;
|
||||
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.JingleS5BTransportManager;
|
||||
import org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferManager;
|
||||
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFile;
|
||||
import org.jivesoftware.smackx.jingle_filetransfer.controller.IncomingFileOfferController;
|
||||
import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingFileOfferListener;
|
||||
import org.jivesoftware.smackx.jingle_filetransfer.listener.ProgressListener;
|
||||
import org.jivesoftware.smackx.omemo.AbstractOmemoIntegrationTest;
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
import org.jivesoftware.smackx.omemo.OmemoService;
|
||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.provider.OmemoVAxolotlProvider;
|
||||
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
|
||||
|
||||
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||
import org.igniterealtime.smack.inttest.TestNotPossibleException;
|
||||
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
||||
|
||||
public class JetIntegrationTest extends AbstractOmemoIntegrationTest {
|
||||
|
||||
private OmemoManager oa, ob;
|
||||
private JetManager ja, jb;
|
||||
private JingleIBBTransportManager ia, ib;
|
||||
private JingleS5BTransportManager sa, sb;
|
||||
private OmemoStore<?,?,?,?,?,?,?,?,?> store;
|
||||
|
||||
public JetIntegrationTest(SmackIntegrationTestEnvironment environment)
|
||||
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
||||
SmackException.NoResponseException, TestNotPossibleException {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void before() {
|
||||
store = OmemoService.getInstance().getOmemoStoreBackend();
|
||||
oa = OmemoManager.getInstanceFor(conOne, 666);
|
||||
ob = OmemoManager.getInstanceFor(conTwo, 777);
|
||||
ja = JetManager.getInstanceFor(conOne);
|
||||
jb = JetManager.getInstanceFor(conTwo);
|
||||
ia = JingleIBBTransportManager.getInstanceFor(conOne);
|
||||
ib = JingleIBBTransportManager.getInstanceFor(conTwo);
|
||||
sa = JingleS5BTransportManager.getInstanceFor(conOne);
|
||||
sb = JingleS5BTransportManager.getInstanceFor(conTwo);
|
||||
JetManager.registerEnvelopeProvider(OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL, new OmemoVAxolotlProvider());
|
||||
}
|
||||
|
||||
@SmackIntegrationTest
|
||||
public void JingleEncryptedFileTransferTest()
|
||||
throws Exception {
|
||||
|
||||
final SimpleResultSyncPoint received = new SimpleResultSyncPoint();
|
||||
|
||||
Random weakRandom = new Random();
|
||||
|
||||
//Setup OMEMO
|
||||
subscribe(oa, ob, "Bob");
|
||||
subscribe(ob, oa, "Alice");
|
||||
setUpOmemoManager(oa);
|
||||
setUpOmemoManager(ob);
|
||||
unidirectionalTrust(oa, ob);
|
||||
unidirectionalTrust(ob, oa);
|
||||
|
||||
ja.registerEnvelopeManager(oa);
|
||||
jb.registerEnvelopeManager(ob);
|
||||
|
||||
byte[] sourceBytes = new byte[16000];
|
||||
weakRandom.nextBytes(sourceBytes);
|
||||
InputStream sourceStream = new ByteArrayInputStream(sourceBytes);
|
||||
final ByteArrayOutputStream targetStream = new ByteArrayOutputStream(16000);
|
||||
|
||||
JingleFileTransferManager.getInstanceFor(conTwo).addIncomingFileOfferListener(new IncomingFileOfferListener() {
|
||||
@Override
|
||||
public void onIncomingFileOffer(IncomingFileOfferController offer) {
|
||||
try {
|
||||
offer.addProgressListener(new ProgressListener() {
|
||||
@Override
|
||||
public void started() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void progress(float percent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finished() {
|
||||
received.signal();
|
||||
}
|
||||
});
|
||||
offer.accept(conTwo, targetStream);
|
||||
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException | IOException e) {
|
||||
received.signal(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ja.sendEncryptedStream(sourceStream, new JingleFile("test", "desc", (long) sourceBytes.length, null, null, null), conTwo.getUser().asFullJidOrThrow(), oa);
|
||||
|
||||
received.waitForResult(60 * 1000);
|
||||
|
||||
assertArrayEquals(sourceBytes, targetStream.toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void after() {
|
||||
oa.shutdown();
|
||||
ob.shutdown();
|
||||
cleanServerSideTraces(oa);
|
||||
cleanServerSideTraces(ob);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests for XEP-XXXX - Jingle Encrypted Transfers.
|
||||
*/
|
||||
package org.jivesoftware.smackx.jet;
|
|
@ -28,7 +28,6 @@ import org.jivesoftware.smack.SmackException;
|
|||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.roster.Roster;
|
||||
import org.jivesoftware.smack.roster.RosterEntry;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||
|
@ -41,11 +40,11 @@ import org.jivesoftware.smackx.pubsub.PubSubManager;
|
|||
/**
|
||||
* Class containing some helper methods for OmemoIntegrationTests.
|
||||
*/
|
||||
final class OmemoIntegrationTestHelper {
|
||||
public final class OmemoIntegrationTestHelper {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(OmemoIntegrationTestHelper.class.getSimpleName());
|
||||
|
||||
static void cleanServerSideTraces(OmemoManager omemoManager) {
|
||||
public static void cleanServerSideTraces(OmemoManager omemoManager) {
|
||||
cleanUpPubSub(omemoManager);
|
||||
cleanUpRoster(omemoManager);
|
||||
}
|
||||
|
@ -115,7 +114,7 @@ final class OmemoIntegrationTestHelper {
|
|||
* @throws InterruptedException
|
||||
* @throws SmackException.NoResponseException
|
||||
*/
|
||||
static void subscribe(OmemoManager alice, OmemoManager bob, String nick)
|
||||
public static void subscribe(OmemoManager alice, OmemoManager bob, String nick)
|
||||
throws SmackException.NotLoggedInException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, InterruptedException,
|
||||
SmackException.NoResponseException {
|
||||
|
@ -127,7 +126,7 @@ final class OmemoIntegrationTestHelper {
|
|||
}
|
||||
|
||||
|
||||
static void unidirectionalTrust(OmemoManager alice, OmemoManager bob) throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, CannotEstablishOmemoSessionException {
|
||||
public static void unidirectionalTrust(OmemoManager alice, OmemoManager bob) throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, CannotEstablishOmemoSessionException {
|
||||
//Fetch deviceList
|
||||
alice.requestDeviceListUpdateFor(bob.getOwnJid());
|
||||
LOGGER.log(Level.INFO, "Current deviceList state: " + alice.getOwnDevice() + " knows " + bob.getOwnDevice() + ": "
|
||||
|
@ -147,7 +146,7 @@ final class OmemoIntegrationTestHelper {
|
|||
|
||||
}
|
||||
|
||||
static void setUpOmemoManager(OmemoManager omemoManager) throws CorruptedOmemoKeyException, InterruptedException, SmackException.NoResponseException, SmackException.NotConnectedException, XMPPException.XMPPErrorException, SmackException.NotLoggedInException, PubSubException.NotALeafNodeException, NotAPubSubNodeException {
|
||||
public static void setUpOmemoManager(OmemoManager omemoManager) throws CorruptedOmemoKeyException, InterruptedException, SmackException.NoResponseException, SmackException.NotConnectedException, XMPPException.XMPPErrorException, SmackException.NotLoggedInException, PubSubException.NotALeafNodeException, NotAPubSubNodeException {
|
||||
omemoManager.initialize();
|
||||
OmemoBundleElement bundle = OmemoService.fetchBundle(omemoManager, omemoManager.getOwnDevice());
|
||||
assertNotNull("Bundle must not be null.", bundle);
|
||||
|
|
|
@ -22,6 +22,7 @@ import static junit.framework.TestCase.assertNotNull;
|
|||
import static junit.framework.TestCase.assertNotSame;
|
||||
import static junit.framework.TestCase.assertNull;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.cleanServerSideTraces;
|
||||
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.deletePath;
|
||||
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.setUpOmemoManager;
|
||||
|
||||
|
@ -154,6 +155,8 @@ public class OmemoStoreTest extends AbstractOmemoIntegrationTest {
|
|||
|
||||
@Override
|
||||
public void after() {
|
||||
cleanServerSideTraces(alice);
|
||||
cleanServerSideTraces(bob);
|
||||
alice.shutdown();
|
||||
bob.shutdown();
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public class SmackOmemoSignalIntegrationTestFramework {
|
|||
SignalOmemoService.acknowledgeLicense();
|
||||
SignalOmemoService.setup();
|
||||
|
||||
final String[] smackOmemoPackages = new String[] { "org.jivesoftware.smackx.omemo" };
|
||||
final String[] smackOmemoPackages = new String[] { "org.jivesoftware.smackx.omemo", "org.jivesoftware.smackx.jet" };
|
||||
SmackIntegrationTestFramework.main(smackOmemoPackages);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,11 +41,12 @@ import org.jivesoftware.smack.packet.ExtensionElement;
|
|||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.util.Async;
|
||||
|
||||
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.jet.JingleEnvelopeManager;
|
||||
import org.jivesoftware.smackx.mam.MamManager;
|
||||
import org.jivesoftware.smackx.muc.MultiUserChat;
|
||||
import org.jivesoftware.smackx.muc.MultiUserChatManager;
|
||||
|
@ -66,6 +67,7 @@ import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
|||
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
||||
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
|
||||
import org.jivesoftware.smackx.omemo.listener.OmemoMucMessageListener;
|
||||
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
|
||||
import org.jivesoftware.smackx.pep.PEPListener;
|
||||
import org.jivesoftware.smackx.pep.PEPManager;
|
||||
import org.jivesoftware.smackx.pubsub.EventElement;
|
||||
|
@ -78,6 +80,7 @@ import org.jxmpp.jid.BareJid;
|
|||
import org.jxmpp.jid.DomainBareJid;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.jxmpp.jid.EntityFullJid;
|
||||
import org.jxmpp.jid.FullJid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
|
||||
|
@ -88,7 +91,7 @@ import org.jxmpp.stringprep.XmppStringprepException;
|
|||
* @author Paul Schaub
|
||||
*/
|
||||
|
||||
public final class OmemoManager extends Manager {
|
||||
public final class OmemoManager extends Manager implements JingleEnvelopeManager {
|
||||
private static final Logger LOGGER = Logger.getLogger(OmemoManager.class.getName());
|
||||
|
||||
private static final WeakHashMap<XMPPConnection, WeakHashMap<Integer,OmemoManager>> INSTANCES = new WeakHashMap<>();
|
||||
|
@ -133,6 +136,7 @@ public final class OmemoManager extends Manager {
|
|||
});
|
||||
|
||||
service = OmemoService.getInstance();
|
||||
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -751,10 +755,20 @@ public final class OmemoManager extends Manager {
|
|||
*
|
||||
* @return the connection of this manager
|
||||
*/
|
||||
XMPPConnection getConnection() {
|
||||
@Override
|
||||
public XMPPConnection getConnection() {
|
||||
return connection();
|
||||
}
|
||||
|
||||
public static String getNamespace() {
|
||||
return OMEMO_NAMESPACE_V_AXOLOTL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJingleEnvelopeNamespace() {
|
||||
return getNamespace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the OMEMO service object.
|
||||
*
|
||||
|
@ -840,4 +854,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 envelope) throws JingleEncryptionException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||
if (!envelope.getNamespace().equals(OMEMO_NAMESPACE_V_AXOLOTL)
|
||||
|| !envelope.getElementName().equals(OmemoElement.ENCRYPTED)) {
|
||||
throw new IllegalArgumentException("Passed ExtensionElement MUST be an OmemoElement!");
|
||||
}
|
||||
|
||||
OmemoElement omemoElement = (OmemoElement) envelope;
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@ import org.jivesoftware.smack.packet.Message;
|
|||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smack.util.Async;
|
||||
|
||||
import org.jivesoftware.smackx.carbons.CarbonCopyReceivedListener;
|
||||
import org.jivesoftware.smackx.carbons.CarbonManager;
|
||||
import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
|
||||
|
|
Loading…
Reference in a new issue