Rename store methods and fix access to open pubsub nodes

This commit is contained in:
Paul Schaub 2018-07-03 11:29:27 +02:00
parent 6a756c9a44
commit 613c5d869a
10 changed files with 110 additions and 60 deletions

View File

@ -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();

View File

@ -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()));
}
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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);

View File

@ -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<OpenPgpV4Fingerprint> announced = provider.getStore().getAnnouncedKeysFingerprints(jid).keySet();
Set<OpenPgpV4Fingerprint> available = provider.getStore().getAvailableKeysFingerprints(jid).keySet();
Map<OpenPgpV4Fingerprint, Throwable> 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);
}

View File

@ -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 <a href="https://xmpp.org/extensions/xep-0373.html#exchange">XEP-0373 §3.1</a>

View File

@ -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;

View File

@ -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;
}

View File

@ -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<PayloadItem<PublicKeysListElement>> 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<PayloadItem<PubkeyElement>> 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 <a href="https://github.com/processone/ejabberd/issues/2483">Ejabberd bug tracker about the issue</a>
*
* @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<String, Node> 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<PubSubException.NotALeafNodeException> 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<LeafNode> 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);
}
}
}