diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/BasicOpenPgpInstantMessagingIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/BasicOpenPgpInstantMessagingIntegrationTest.java index 2f126bfc5..1d6497b12 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/BasicOpenPgpInstantMessagingIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/BasicOpenPgpInstantMessagingIntegrationTest.java @@ -24,7 +24,6 @@ import java.util.logging.Level; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smack.util.FileUtils; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.ox.OXInstantMessagingManager; @@ -76,15 +75,6 @@ public class BasicOpenPgpInstantMessagingIntegrationTest extends AbstractOpenPgp final SimpleResultSyncPoint bobReceivedMessage = new SimpleResultSyncPoint(); final String body = "Writing integration tests is an annoying task, but it has to be done, so lets do it!!!"; - Roster aliceRoster = Roster.getInstanceFor(aliceConnection); - Roster bobRoster = Roster.getInstanceFor(bobConnection); - - aliceRoster.setSubscriptionMode(Roster.SubscriptionMode.accept_all); - bobRoster.setSubscriptionMode(Roster.SubscriptionMode.accept_all); - - aliceRoster.createEntry(bob, "Bob", null); - bobRoster.createEntry(alice, "Alice", null); - FileBasedPainlessOpenPgpStore aliceStore = new FileBasedPainlessOpenPgpStore(aliceStorePath, new UnprotectedKeysProtector()); FileBasedPainlessOpenPgpStore bobStore = new FileBasedPainlessOpenPgpStore(bobStorePath, new UnprotectedKeysProtector()); @@ -114,8 +104,8 @@ public class BasicOpenPgpInstantMessagingIntegrationTest extends AbstractOpenPgp aliceFingerprint = aliceOpenPgp.generateAndImportKeyPair(alice); bobFingerprint = bobOpenPgp.generateAndImportKeyPair(bob); - aliceStore.setPrimaryOpenPgpKeyPairFingerprint(aliceFingerprint); - bobStore.setPrimaryOpenPgpKeyPairFingerprint(bobFingerprint); + aliceStore.setSigningKeyPairFingerprint(aliceFingerprint); + bobStore.setSigningKeyPairFingerprint(bobFingerprint); aliceOpenPgp.announceSupportAndPublish(); bobOpenPgp.announceSupportAndPublish(); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/SecretKeyBackupRestoreIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/SecretKeyBackupRestoreIntegrationTest.java index a06cc01a8..e97fdba0b 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/SecretKeyBackupRestoreIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/SecretKeyBackupRestoreIntegrationTest.java @@ -101,11 +101,11 @@ public class SecretKeyBackupRestoreIntegrationTest extends AbstractOpenPgpIntegr OpenPgpManager openPgpManager = OpenPgpManager.getInstanceFor(aliceConnection); openPgpManager.setOpenPgpProvider(beforeProvider); - assertNull(beforeStore.getPrimaryOpenPgpKeyPairFingerprint()); + assertNull(beforeStore.getSigningKeyPairFingerprint()); OpenPgpV4Fingerprint keyFingerprint = openPgpManager.generateAndImportKeyPair(alice); - beforeStore.setPrimaryOpenPgpKeyPairFingerprint(keyFingerprint); - assertEquals(keyFingerprint, beforeStore.getPrimaryOpenPgpKeyPairFingerprint()); + beforeStore.setSigningKeyPairFingerprint(keyFingerprint); + assertEquals(keyFingerprint, beforeStore.getSigningKeyPairFingerprint()); assertTrue(beforeStore.getAvailableKeyPairFingerprints(alice).contains(keyFingerprint)); @@ -125,7 +125,7 @@ public class SecretKeyBackupRestoreIntegrationTest extends AbstractOpenPgpIntegr PainlessOpenPgpProvider afterProvider = new PainlessOpenPgpProvider(alice, afterStore); openPgpManager.setOpenPgpProvider(afterProvider); - assertNull(afterStore.getPrimaryOpenPgpKeyPairFingerprint()); + assertNull(afterStore.getSigningKeyPairFingerprint()); assertFalse(afterStore.getAvailableKeyPairFingerprints(alice).contains(keyFingerprint)); OpenPgpV4Fingerprint fingerprint = openPgpManager.restoreSecretKeyServerBackup(new AskForBackupCodeCallback() { @@ -142,9 +142,9 @@ public class SecretKeyBackupRestoreIntegrationTest extends AbstractOpenPgpIntegr assertTrue(afterStore.getAvailableKeyPairFingerprints(alice).contains(keyFingerprint)); - afterStore.setPrimaryOpenPgpKeyPairFingerprint(fingerprint); + afterStore.setSigningKeyPairFingerprint(fingerprint); - assertEquals(keyFingerprint, afterStore.getPrimaryOpenPgpKeyPairFingerprint()); + assertEquals(keyFingerprint, afterStore.getSigningKeyPairFingerprint()); assertTrue(Arrays.equals(beforeStore.getSecretKeyRings(alice).getEncoded(), afterStore.getSecretKeyRings(alice).getEncoded())); } } diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/AbstractPainlessOpenPgpStore.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/AbstractPainlessOpenPgpStore.java index 305d34c36..185631f04 100644 --- a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/AbstractPainlessOpenPgpStore.java +++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/AbstractPainlessOpenPgpStore.java @@ -39,12 +39,12 @@ public abstract class AbstractPainlessOpenPgpStore implements PainlessOpenPgpSto private final SecretKeyRingProtector secretKeyRingProtector; @Override - public OpenPgpV4Fingerprint getPrimaryOpenPgpKeyPairFingerprint() { + public OpenPgpV4Fingerprint getSigningKeyPairFingerprint() { return primaryKeyFingerprint; } @Override - public void setPrimaryOpenPgpKeyPairFingerprint(OpenPgpV4Fingerprint fingerprint) { + public void setSigningKeyPairFingerprint(OpenPgpV4Fingerprint fingerprint) { this.primaryKeyFingerprint = fingerprint; } diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/PainlessOpenPgpProvider.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/PainlessOpenPgpProvider.java index 8fe08f618..32b3b8067 100644 --- a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/PainlessOpenPgpProvider.java +++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/PainlessOpenPgpProvider.java @@ -220,7 +220,7 @@ public class PainlessOpenPgpProvider implements OpenPgpProvider { secretKeyRings = getStore().getSecretKeyRings(owner); } catch (PGPException | IOException e) { LOGGER.log(Level.INFO, "Could not get secret keys of user " + owner); - throw new MissingOpenPgpKeyPairException(owner, getStore().getPrimaryOpenPgpKeyPairFingerprint()); + throw new MissingOpenPgpKeyPairException(owner, getStore().getSigningKeyPairFingerprint()); } SecretKeyRingProtector protector = getStore().getSecretKeyProtector(); diff --git a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/DryOxEncryptionTest.java b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/DryOxEncryptionTest.java index a824b0505..0223290c8 100644 --- a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/DryOxEncryptionTest.java +++ b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/DryOxEncryptionTest.java @@ -90,8 +90,8 @@ public class DryOxEncryptionTest extends OxTestSuite { OpenPgpV4Fingerprint romeoFinger = romeoProvider.importSecretKey(romemo, BCUtil.getDecodedBytes(TestKeys.ROMEO_PRIV.getBytes(UTF8))); - julietStore.setPrimaryOpenPgpKeyPairFingerprint(julietFinger); - romeoStore.setPrimaryOpenPgpKeyPairFingerprint(romeoFinger); + julietStore.setSigningKeyPairFingerprint(julietFinger); + romeoStore.setSigningKeyPairFingerprint(romeoFinger); byte[] julietPubBytes = julietStore.getPublicKeyRingBytes(juliet, julietFinger); byte[] romeoPubBytes = romeoStore.getPublicKeyRingBytes(romemo, romeoFinger); diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java index 0eab63ddc..6018fe590 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java @@ -219,7 +219,7 @@ public final class OpenPgpManager extends Manager { */ public OpenPgpV4Fingerprint getOurFingerprint() { throwIfNoProviderSet(); - return provider.getStore().getPrimaryOpenPgpKeyPairFingerprint(); + return provider.getStore().getSigningKeyPairFingerprint(); } /** @@ -362,12 +362,9 @@ public final class OpenPgpManager extends Manager { * @throws SmackOpenPgpException * @throws InterruptedException * @throws XMPPException.XMPPErrorException - * @throws SmackException.NotConnectedException - * @throws SmackException.NoResponseException */ private OpenPgpFingerprints determineContactsKeys(BareJid jid) - throws SmackOpenPgpException, InterruptedException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, SmackException.NoResponseException { + throws SmackOpenPgpException, InterruptedException, XMPPException.XMPPErrorException { Set announced = provider.getStore().getAnnouncedKeysFingerprints(jid).keySet(); Set available = provider.getStore().getAvailableKeysFingerprints(jid).keySet(); Map unfetched = new HashMap<>(); @@ -382,7 +379,7 @@ public final class OpenPgpManager extends Manager { processPublicKey(pubkeyElement, jid); available.add(f); - } catch (PubSubException.NotAPubSubNodeException | PubSubException.NotALeafNodeException e) { + } catch (SmackException e) { LOGGER.log(Level.WARNING, "Could not fetch public key " + f.toString() + " of user " + jid.toString(), e); unfetched.put(f, e); } catch (MissingUserIdOnKeyException e) { @@ -422,9 +419,8 @@ public final class OpenPgpManager extends Manager { }; public void requestMetadataUpdate(BareJid contact) - throws InterruptedException, PubSubException.NotALeafNodeException, SmackException.NoResponseException, - SmackException.NotConnectedException, XMPPException.XMPPErrorException, - PubSubException.NotAPubSubNodeException { + throws InterruptedException, SmackException, + XMPPException.XMPPErrorException { PublicKeysListElement metadata = PubSubDelegate.fetchPubkeysList(connection(), contact); processPublicKeysListElement(contact, metadata); } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java index 8b3b087c5..52fb2fcc2 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java @@ -64,7 +64,7 @@ public interface OpenPgpProvider { throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException, SmackOpenPgpException, IOException; /** - * Sign a {@link SignElement} with th users signing key. + * Sign a {@link SignElement} with the users signing key. * The resulting byte array contains the signed byte representation of the {@link SignElement}. * * @see XEP-0373 ยง3.1 diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java index 6ea5bd6d4..6623f8d18 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java @@ -37,7 +37,7 @@ public interface OpenPgpStore { * * @return fingerprint of the primary OpenPGP key pair. */ - OpenPgpV4Fingerprint getPrimaryOpenPgpKeyPairFingerprint(); + OpenPgpV4Fingerprint getSigningKeyPairFingerprint(); /** * Set the {@link OpenPgpV4Fingerprint} of the primary OpenPGP key pair. @@ -45,7 +45,7 @@ public interface OpenPgpStore { * * @param fingerprint {@link OpenPgpV4Fingerprint} of the new primary key pair. */ - void setPrimaryOpenPgpKeyPairFingerprint(OpenPgpV4Fingerprint fingerprint); + void setSigningKeyPairFingerprint(OpenPgpV4Fingerprint fingerprint); /** * Return a {@link Set} containing the {@link OpenPgpV4Fingerprint}s of the master keys of all available @@ -127,6 +127,8 @@ public interface OpenPgpStore { * @param owner owner of the key * @param fingerprint fingerprint of the key * @return byte representation of the public key. + * + * @throws MissingOpenPgpPublicKeyException if the key does not exist. */ byte[] getPublicKeyRingBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws MissingOpenPgpPublicKeyException; @@ -137,6 +139,8 @@ public interface OpenPgpStore { * @param owner owner of the key * @param fingerprint fingerprint of the key * @return byte representation of the secret key. + * + * @throws MissingOpenPgpKeyPairException if the secret key doesn't exist. */ byte[] getSecretKeyRingBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws MissingOpenPgpKeyPairException; diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpContact.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpContact.java index 7c97b76b3..8cb135cb5 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpContact.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpContact.java @@ -57,7 +57,7 @@ public class OpenPgpContact { OpenPgpFingerprints contactsFingerprints) { this.cryptoProvider = cryptoProvider; this.jid = jid; - this.singingKey = cryptoProvider.getStore().getPrimaryOpenPgpKeyPairFingerprint(); + this.singingKey = cryptoProvider.getStore().getSigningKeyPairFingerprint(); this.ourFingerprints = ourFingerprints; this.contactsFingerprints = contactsFingerprints; } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/PubSubDelegate.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/PubSubDelegate.java index 483b49833..a220f63ce 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/PubSubDelegate.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/PubSubDelegate.java @@ -16,8 +16,12 @@ */ package org.jivesoftware.smackx.ox.util; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -35,6 +39,7 @@ import org.jivesoftware.smackx.pubsub.AccessModel; import org.jivesoftware.smackx.pubsub.ConfigureForm; import org.jivesoftware.smackx.pubsub.Item; import org.jivesoftware.smackx.pubsub.LeafNode; +import org.jivesoftware.smackx.pubsub.Node; import org.jivesoftware.smackx.pubsub.PayloadItem; import org.jivesoftware.smackx.pubsub.PubSubException; import org.jivesoftware.smackx.pubsub.PubSubManager; @@ -157,19 +162,16 @@ public class PubSubDelegate { * * @return content of our metadata node. * @throws InterruptedException - * @throws PubSubException.NotALeafNodeException - * @throws SmackException.NoResponseException - * @throws SmackException.NotConnectedException + * @throws SmackException * @throws XMPPException.XMPPErrorException - * @throws PubSubException.NotAPubSubNodeException */ public static PublicKeysListElement fetchPubkeysList(XMPPConnection connection) - throws InterruptedException, PubSubException.NotALeafNodeException, SmackException.NoResponseException, - SmackException.NotConnectedException, XMPPException.XMPPErrorException, - PubSubException.NotAPubSubNodeException { + throws InterruptedException, SmackException, + XMPPException.XMPPErrorException { return fetchPubkeysList(connection, connection.getUser().asBareJid()); } + /** * Consult the public key metadata node of {@code contact} to fetch the list of their published OpenPGP public keys. * TODO: Add @see which points to the (for now missing) respective example in XEP-0373. @@ -177,19 +179,15 @@ public class PubSubDelegate { * @param contact {@link BareJid} of the user we want to fetch the list from. * @return content of {@code contact}'s metadata node. * @throws InterruptedException - * @throws PubSubException.NotALeafNodeException - * @throws SmackException.NoResponseException - * @throws SmackException.NotConnectedException + * @throws SmackException * @throws XMPPException.XMPPErrorException - * @throws PubSubException.NotAPubSubNodeException */ public static PublicKeysListElement fetchPubkeysList(XMPPConnection connection, BareJid contact) - throws InterruptedException, PubSubException.NotALeafNodeException, SmackException.NoResponseException, - SmackException.NotConnectedException, XMPPException.XMPPErrorException, - PubSubException.NotAPubSubNodeException { + throws InterruptedException, SmackException, + XMPPException.XMPPErrorException { PubSubManager pm = PubSubManager.getInstance(connection, contact); - LeafNode node = pm.getLeafNode(PEP_NODE_PUBLIC_KEYS); + LeafNode node = getOpenLeafNode(pm, PEP_NODE_PUBLIC_KEYS); List> list = node.getItems(1); if (list.isEmpty()) { @@ -247,6 +245,7 @@ public class PubSubDelegate { } } + /** * Fetch the OpenPGP public key of a {@code contact}, identified by its OpenPGP {@code v4_fingerprint}. * @@ -255,20 +254,16 @@ public class PubSubDelegate { * @param contact {@link BareJid} of the contact we want to fetch a key from. * @param v4_fingerprint upper case, hex encoded v4 fingerprint of the contacts key. * @return {@link PubkeyElement} containing the requested public key. - * @throws InterruptedException - * @throws PubSubException.NotALeafNodeException - * @throws SmackException.NoResponseException - * @throws SmackException.NotConnectedException + * + * @throws InterruptedException if we get interrupted. + * @throws SmackException in case the node cannot be fetched. * @throws XMPPException.XMPPErrorException - * @throws PubSubException.NotAPubSubNodeException */ public static PubkeyElement fetchPubkey(XMPPConnection connection, BareJid contact, OpenPgpV4Fingerprint v4_fingerprint) - throws InterruptedException, PubSubException.NotALeafNodeException, SmackException.NoResponseException, - SmackException.NotConnectedException, XMPPException.XMPPErrorException, - PubSubException.NotAPubSubNodeException { + throws InterruptedException, SmackException, XMPPException.XMPPErrorException { PubSubManager pm = PubSubManager.getInstance(connection, contact); - LeafNode node = pm.getLeafNode(PEP_NODE_PUBLIC_KEY(v4_fingerprint)); + LeafNode node = getOpenLeafNode(pm, PEP_NODE_PUBLIC_KEY(v4_fingerprint)); List> list = node.getItems(1); if (list.isEmpty()) { @@ -352,4 +347,69 @@ public class PubSubDelegate { PubSubManager pm = PubSubManager.getInstance(connection); pm.deleteNode(PEP_NODE_SECRET_KEY); } + + /** + * Use reflection magic to get a {@link LeafNode} without doing a disco#info query. + * This method is useful for fetching nodes that are configured with the access model 'open', since + * some servers that announce support for that access model do not allow disco#info queries from contacts + * which are not subscribed to the node owner. Therefore this method fetches the node directly and puts it + * into the {@link PubSubManager}s node map. + * + * @see Ejabberd bug tracker about the issue + * + * @param pubSubManager pubsub manager + * @param nodeName name of the node + * @return leafNode + * + * @throws SmackException if something goes wrong with reflections. + * @throws PubSubException.NotALeafNodeException if the node is already in the nodeMap, but is NOT a LeafNode. + */ + @SuppressWarnings("unchecked") + public static LeafNode getOpenLeafNode(PubSubManager pubSubManager, String nodeName) + throws SmackException, PubSubException.NotALeafNodeException { + + try { + + // Get access to the PubSubManager's nodeMap + Field field = pubSubManager.getClass().getDeclaredField("nodeMap"); + field.setAccessible(true); +// CHECKSTYLE:OFF + Map nodeMap = (Map) field.get(pubSubManager); +// CHECKSTYLE:ON + + // Check, if the node already exists + Node existingNode = nodeMap.get(nodeName); + if (existingNode != null) { + + if (existingNode instanceof LeafNode) { + // We already know that node + return (LeafNode) existingNode; + + } else { + // Throw a new NotALeafNodeException, as the node is not a LeafNode. + // Again use reflections to access the exceptions constructor. + Constructor exceptionConstructor = + PubSubException.NotALeafNodeException.class.getDeclaredConstructor(String.class, BareJid.class); + exceptionConstructor.setAccessible(true); + throw exceptionConstructor.newInstance(nodeName, pubSubManager.getServiceJid()); + } + } + + // Node does not exist. Create the node + Constructor constructor; + constructor = LeafNode.class.getDeclaredConstructor(PubSubManager.class, String.class); + constructor.setAccessible(true); + LeafNode node = constructor.newInstance(pubSubManager, nodeName); + + // Add it to the node map + nodeMap.put(nodeName, node); + + // And return + return node; + + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException | + NoSuchFieldException e) { + throw new SmackException("Using reflections to create a LeafNode and put it into PubSubManagers nodeMap failed.", e); + } + } }