Refactor JET in order to match spec

This commit is contained in:
vanitasvitae 2017-08-16 16:42:11 +02:00
parent 5edd630fd0
commit eb413f30b1
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
9 changed files with 78 additions and 70 deletions

View File

@ -50,8 +50,8 @@ public final class JetManager extends Manager implements JingleDescriptionManage
private static final Logger LOGGER = Logger.getLogger(JetManager.class.getName());
private static final WeakHashMap<XMPPConnection, JetManager> INSTANCES = new WeakHashMap<>();
private static final HashMap<String, JingleEncryptionMethod> encryptionMethods = new HashMap<>();
private static final HashMap<String, ExtensionElementProvider<?>> encryptionMethodProviders = new HashMap<>();
private static final HashMap<String, JingleEnvelopeManager> envelopeManagers = new HashMap<>();
private static final HashMap<String, ExtensionElementProvider<?>> envelopeProviders = new HashMap<>();
private final JingleManager jingleManager;
@ -78,17 +78,17 @@ public final class JetManager extends Manager implements JingleDescriptionManage
return manager;
}
public OutgoingFileOfferController sendEncryptedFile(File file, FullJid recipient, JingleEncryptionMethod method) throws Exception {
return sendEncryptedFile(file, null, recipient, method);
public OutgoingFileOfferController sendEncryptedFile(File file, FullJid recipient, JingleEnvelopeManager envelopeManager) throws Exception {
return sendEncryptedFile(file, null, recipient, envelopeManager);
}
public OutgoingFileOfferController sendEncryptedFile(File file, String filename, FullJid recipient, JingleEncryptionMethod method) throws Exception {
public OutgoingFileOfferController sendEncryptedFile(File file, String filename, FullJid recipient, JingleEnvelopeManager envelopeManager) throws Exception {
if (file == null || !file.exists()) {
throw new IllegalArgumentException("File MUST NOT be null and MUST exist.");
}
ServiceDiscoveryManager disco = ServiceDiscoveryManager.getInstanceFor(connection());
if (!disco.supportsFeature(recipient, getNamespace()) || !disco.supportsFeature(recipient, method.getNamespace())) {
if (!disco.supportsFeature(recipient, getNamespace()) || !disco.supportsFeature(recipient, envelopeManager.getJingleEnvelopeNamespace())) {
throw new SmackException.FeatureNotSupportedException(getNamespace(), recipient);
}
@ -106,35 +106,35 @@ public final class JetManager extends Manager implements JingleDescriptionManage
JingleTransportManager transportManager = jingleManager.getBestAvailableTransportManager(recipient);
content.setTransport(transportManager.createTransportForInitiator(content));
JetSecurity security = new JetSecurity(method, recipient, content.getName(), Aes256GcmNoPadding.NAMESPACE);
JetSecurity security = new JetSecurity(envelopeManager, recipient, content.getName(), Aes256GcmNoPadding.NAMESPACE);
content.setSecurity(security);
session.sendInitiate(connection());
return offer;
}
public void registerEncryptionMethod(JingleEncryptionMethod method) {
encryptionMethods.put(method.getNamespace(), method);
public void registerEnvelopeManager(JingleEnvelopeManager method) {
envelopeManagers.put(method.getJingleEnvelopeNamespace(), method);
}
public void unregisterEncryptionMethod(String namespace) {
encryptionMethods.remove(namespace);
public void unregisterEnvelopeManager(String namespace) {
envelopeManagers.remove(namespace);
}
public JingleEncryptionMethod getEncryptionMethod(String namespace) {
return encryptionMethods.get(namespace);
public JingleEnvelopeManager getEnvelopeManager(String namespace) {
return envelopeManagers.get(namespace);
}
public static void registerEncryptionMethodProvider(String namespace, ExtensionElementProvider<?> provider) {
encryptionMethodProviders.put(namespace, provider);
public static void registerEnvelopeProvider(String namespace, ExtensionElementProvider<?> provider) {
envelopeProviders.put(namespace, provider);
}
public static void removeEncryptionMethodProvider(String namespace) {
encryptionMethodProviders.remove(namespace);
public static void unregisterEnvelopeProvider(String namespace) {
envelopeProviders.remove(namespace);
}
public static ExtensionElementProvider<?> getEncryptionMethodProvider(String namespace) {
return encryptionMethodProviders.get(namespace);
public static ExtensionElementProvider<?> getEnvelopeProvider(String namespace) {
return envelopeProviders.get(namespace);
}
@Override

View File

@ -28,11 +28,15 @@ import org.jxmpp.jid.FullJid;
/**
* Classes that implement this interface can be used to encrypt Jingle File Transfers.
*/
public interface JingleEncryptionMethod {
public interface JingleEnvelopeManager {
ExtensionElement encryptJingleTransfer(FullJid recipient, byte[] keyData) throws JingleEncryptionException, InterruptedException, NoSuchAlgorithmException, SmackException.NotConnectedException, SmackException.NoResponseException;
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;
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;
@ -44,5 +48,5 @@ public interface JingleEncryptionMethod {
XMPPConnection getConnection();
String getNamespace();
String getJingleEnvelopeNamespace();
}

View File

@ -31,7 +31,7 @@ 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.JingleEncryptionMethod;
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;
@ -49,7 +49,7 @@ public class JetSecurity extends JingleSecurity<JetSecurityElement> {
public static final String NAMESPACE_V0 = "urn:xmpp:jingle:jet:0";
public static final String NAMESPACE = NAMESPACE_V0;
private final String methodNamespace;
private final String envelopeNamespace;
private AesGcmNoPadding aesKey;
private final ExtensionElement child;
@ -59,26 +59,26 @@ public class JetSecurity extends JingleSecurity<JetSecurityElement> {
public JetSecurity(JetSecurityElement element) {
super();
this.child = element.getChild();
this.methodNamespace = element.getMethodNamespace();
this.envelopeNamespace = element.getEnvelopeNamespace();
this.contentName = element.getContentName();
this.cipherName = element.getCipherName();
}
public JetSecurity(JingleEncryptionMethod method, FullJid recipient, String contentName, String cipherName)
public JetSecurity(JingleEnvelopeManager envelopeManager, FullJid recipient, String contentName, String cipherName)
throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
InvalidAlgorithmParameterException, InvalidKeyException, InterruptedException,
JingleEncryptionMethod.JingleEncryptionException, SmackException.NotConnectedException,
JingleEnvelopeManager.JingleEncryptionException, SmackException.NotConnectedException,
SmackException.NoResponseException {
this.methodNamespace = method.getNamespace();
this.envelopeNamespace = envelopeManager.getJingleEnvelopeNamespace();
this.aesKey = AesGcmNoPadding.createEncryptionKey(cipherName);
this.child = method.encryptJingleTransfer(recipient, aesKey.getKeyAndIv());
this.child = envelopeManager.encryptJingleTransfer(recipient, aesKey.getKeyAndIv());
this.contentName = contentName;
this.cipherName = cipherName;
}
private void decryptEncryptionKey(JingleEncryptionMethod method, FullJid sender)
throws InterruptedException, JingleEncryptionMethod.JingleEncryptionException, XMPPException.XMPPErrorException,
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);
@ -125,18 +125,18 @@ public class JetSecurity extends JingleSecurity<JetSecurityElement> {
return;
}
JingleEncryptionMethod method = JetManager.getInstanceFor(connection).getEncryptionMethod(getMethodNamespace());
JingleEnvelopeManager method = JetManager.getInstanceFor(connection).getEnvelopeManager(getEnvelopeNamespace());
if (method == null) {
throw new AssertionError("No JingleEncryptionMethodManager found for " + getMethodNamespace());
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 | JingleEncryptionMethod.JingleEncryptionException e) {
} 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 getMethodNamespace() {
return methodNamespace;
public String getEnvelopeNamespace() {
return envelopeNamespace;
}
}

View File

@ -32,16 +32,16 @@ import org.jivesoftware.smackx.jingle.element.JingleContentSecurityElement;
* </jingle>
*/
public class JetSecurityElement extends JingleContentSecurityElement {
public static final String ATTR_NAME = "name";
public static final String ATTR_TYPE = "type";
public static final String ATTR_CIPHER = "cipher";
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 name;
private final String contentName;
private final String cipherName;
public JetSecurityElement(String name, String cipherName, ExtensionElement child) {
this.name = name;
public JetSecurityElement(String contentName, String cipherName, ExtensionElement child) {
this.contentName = contentName;
this.child = child;
this.cipherName = cipherName;
}
@ -49,9 +49,9 @@ public class JetSecurityElement extends JingleContentSecurityElement {
@Override
public CharSequence toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this);
xml.attribute(ATTR_NAME, name)
.attribute(ATTR_CIPHER, cipherName)
.attribute(ATTR_TYPE, child.getNamespace());
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);
@ -63,7 +63,7 @@ public class JetSecurityElement extends JingleContentSecurityElement {
return JetSecurity.NAMESPACE;
}
public String getMethodNamespace() {
public String getEnvelopeNamespace() {
return child.getNamespace();
}
@ -72,7 +72,7 @@ public class JetSecurityElement extends JingleContentSecurityElement {
}
public String getContentName() {
return name;
return contentName;
}
public String getCipherName() {

View File

@ -37,16 +37,16 @@ public class JetSecurityProvider extends JingleContentSecurityProvider<JetSecuri
@Override
public JetSecurityElement parse(XmlPullParser parser, int initialDepth) throws Exception {
String name = parser.getAttributeValue("", JetSecurityElement.ATTR_NAME);
String cipher = parser.getAttributeValue("", JetSecurityElement.ATTR_CIPHER);
String type = parser.getAttributeValue("", JetSecurityElement.ATTR_TYPE);
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.getEncryptionMethodProvider(type);
JetManager.getEnvelopeProvider(type);
if (encryptionElementProvider != null) {
child = encryptionElementProvider.parse(parser);

View File

@ -38,13 +38,13 @@ import org.xml.sax.SAXException;
public class JetElementTest extends SmackTestSuite {
@Test
public void jetTest() throws InterruptedException, JingleEncryptionMethod.JingleEncryptionException, NoSuchAlgorithmException, SmackException.NotConnectedException, SmackException.NoResponseException, IOException, SAXException {
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.getMethodNamespace());
assertEquals(SecurityStub.NAMESPACE, security.getEnvelopeNamespace());
assertEquals(Aes128GcmNoPadding.NAMESPACE, element.getCipherName());
assertEquals(SecurityStub.NAMESPACE, element.getMethodNamespace());
assertEquals(SecurityStub.NAMESPACE, element.getEnvelopeNamespace());
assertEquals("content1", element.getContentName());
String xml = "<security xmlns='" + JetSecurity.NAMESPACE + "' " +
@ -56,7 +56,7 @@ public class JetElementTest extends SmackTestSuite {
assertXMLEqual(xml, security.getElement().toXML().toString());
}
private static class SecurityStub implements JingleEncryptionMethod {
private static class SecurityStub implements JingleEnvelopeManager {
public static final String NAMESPACE = "urn:xmpp:security-stub";
@Override
@ -80,7 +80,7 @@ public class JetElementTest extends SmackTestSuite {
}
@Override
public byte[] decryptJingleTransfer(FullJid sender, ExtensionElement encryptionElement) throws JingleEncryptionException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
public byte[] decryptJingleTransfer(FullJid sender, ExtensionElement envelope) throws JingleEncryptionException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
return new byte[0];
}
@ -90,7 +90,7 @@ public class JetElementTest extends SmackTestSuite {
}
@Override
public String getNamespace() {
public String getJingleEnvelopeNamespace() {
return NAMESPACE;
}
}

View File

@ -298,7 +298,7 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
.setName(name)
.setCreator(creator)
.setSenders(senders)
.setDisposition(disposition);;
.setDisposition(disposition);
if (description != null) {
builder.setDescription(description.getElement());

View File

@ -80,7 +80,7 @@ public class JetIntegrationTest extends AbstractOmemoIntegrationTest {
jb = JetManager.getInstanceFor(conTwo);
ia = JingleIBBTransportManager.getInstanceFor(conOne);
ib = JingleIBBTransportManager.getInstanceFor(conTwo);
JetManager.registerEncryptionMethodProvider(OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL, new OmemoVAxolotlProvider());
JetManager.registerEnvelopeProvider(OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL, new OmemoVAxolotlProvider());
}
@SmackIntegrationTest
@ -97,8 +97,8 @@ public class JetIntegrationTest extends AbstractOmemoIntegrationTest {
unidirectionalTrust(oa, ob);
unidirectionalTrust(ob, oa);
ja.registerEncryptionMethod(oa);
jb.registerEncryptionMethod(ob);
ja.registerEnvelopeManager(oa);
jb.registerEnvelopeManager(ob);
File source = prepareNewTestFile("source");
final File target = new File(tempDir, "target");

View File

@ -46,7 +46,7 @@ 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.JingleEncryptionMethod;
import org.jivesoftware.smackx.jet.JingleEnvelopeManager;
import org.jivesoftware.smackx.mam.MamManager;
import org.jivesoftware.smackx.muc.MultiUserChat;
import org.jivesoftware.smackx.muc.MultiUserChatManager;
@ -91,7 +91,7 @@ import org.jxmpp.stringprep.XmppStringprepException;
* @author Paul Schaub
*/
public final class OmemoManager extends Manager implements JingleEncryptionMethod {
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<>();
@ -760,11 +760,15 @@ public final class OmemoManager extends Manager implements JingleEncryptionMetho
return connection();
}
@Override
public String getNamespace() {
public static String getNamespace() {
return OMEMO_NAMESPACE_V_AXOLOTL;
}
@Override
public String getJingleEnvelopeNamespace() {
return getNamespace();
}
/**
* Return the OMEMO service object.
*
@ -870,13 +874,13 @@ public final class OmemoManager extends Manager implements JingleEncryptionMetho
}
@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)) {
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) encryptionElement;
OmemoElement omemoElement = (OmemoElement) envelope;
Message pseudoMessage = new Message();
pseudoMessage.setFrom(sender.asBareJid());
pseudoMessage.addExtension(omemoElement);