diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/AbstractOpenPgpIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/AbstractOpenPgpIntegrationTest.java index c0b16b997..b32724d52 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/AbstractOpenPgpIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/AbstractOpenPgpIntegrationTest.java @@ -30,7 +30,7 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.jxmpp.jid.BareJid; -public class AbstractOpenPgpIntegrationTest extends AbstractSmackIntegrationTest { +public abstract class AbstractOpenPgpIntegrationTest extends AbstractSmackIntegrationTest { protected final XMPPConnection aliceConnection; protected final XMPPConnection bobConnection; 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 ec18be3ef..e29a0fd6f 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 @@ -16,12 +16,17 @@ */ package org.jivesoftware.smackx.openpgp; +import static junit.framework.TestCase.assertTrue; + import java.io.File; +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; import org.jivesoftware.smackx.ox.OpenPgpManager; import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint; @@ -43,8 +48,8 @@ import org.junit.BeforeClass; public class BasicOpenPgpInstantMessagingIntegrationTest extends AbstractOpenPgpIntegrationTest { - private static final File aliceStorePath = FileUtils.getTempDir("basic_ox_messaging_test_alice"); - private static final File bobStorePath = FileUtils.getTempDir("basic_ox_messaging_test_bob"); + private static final File aliceStorePath = FileUtils.getTempDir("basic_ox_messaging_test_alice_" + StringUtils.randomString(10)); + private static final File bobStorePath = FileUtils.getTempDir("basic_ox_messaging_test_bob_" + StringUtils.randomString(10)); private OpenPgpV4Fingerprint aliceFingerprint = null; private OpenPgpV4Fingerprint bobFingerprint = null; @@ -66,9 +71,20 @@ public class BasicOpenPgpInstantMessagingIntegrationTest extends AbstractOpenPgp public void basicInstantMessagingTest() throws Exception { + LOGGER.log(Level.INFO, aliceStorePath.getAbsolutePath() + " " + bobStorePath.getAbsolutePath()); + 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()); @@ -95,19 +111,25 @@ public class BasicOpenPgpInstantMessagingIntegrationTest extends AbstractOpenPgp aliceOpenPgp.setOpenPgpProvider(aliceProvider); bobOpenPgp.setOpenPgpProvider(bobProvider); - aliceOpenPgp.generateAndImportKeyPair(alice); - bobOpenPgp.generateAndImportKeyPair(bob); + aliceFingerprint = aliceOpenPgp.generateAndImportKeyPair(alice); + bobFingerprint = bobOpenPgp.generateAndImportKeyPair(bob); - aliceFingerprint = aliceOpenPgp.getOurFingerprint(); - bobFingerprint = bobOpenPgp.getOurFingerprint(); + aliceStore.setPrimaryOpenPgpKeyPairFingerprint(aliceFingerprint); + bobStore.setPrimaryOpenPgpKeyPairFingerprint(bobFingerprint); aliceOpenPgp.announceSupportAndPublish(); bobOpenPgp.announceSupportAndPublish(); + LOGGER.log(Level.INFO, "Request metadata of bob"); aliceOpenPgp.requestMetadataUpdate(bob); + LOGGER.log(Level.INFO, "Request metadata of alice"); bobOpenPgp.requestMetadataUpdate(alice); OpenPgpContact bobForAlice = aliceOpenPgp.getOpenPgpContact(bob.asEntityBareJidIfPossible()); + OpenPgpContact aliceForBob = bobOpenPgp.getOpenPgpContact(alice.asEntityBareJidIfPossible()); + + assertTrue(bobForAlice.getFingerprints().getActiveKeys().contains(bobFingerprint)); + assertTrue(aliceForBob.getFingerprints().getActiveKeys().contains(aliceFingerprint)); aliceInstantMessaging.sendOxMessage(bobForAlice, body); 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 new file mode 100644 index 000000000..b724aa886 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/SecretKeyBackupRestoreIntegrationTest.java @@ -0,0 +1,120 @@ +package org.jivesoftware.smackx.openpgp; + +import static junit.framework.TestCase.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.Arrays; +import java.util.Set; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.util.FileUtils; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smackx.ox.OpenPgpManager; +import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint; +import org.jivesoftware.smackx.ox.bouncycastle.FileBasedPainlessOpenPgpStore; +import org.jivesoftware.smackx.ox.bouncycastle.PainlessOpenPgpProvider; +import org.jivesoftware.smackx.ox.callback.AskForBackupCodeCallback; +import org.jivesoftware.smackx.ox.callback.DisplayBackupCodeCallback; +import org.jivesoftware.smackx.ox.callback.SecretKeyBackupSelectionCallback; +import org.jivesoftware.smackx.ox.callback.SecretKeyRestoreSelectionCallback; +import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException; +import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException; +import org.jivesoftware.smackx.ox.exception.NoBackupFoundException; +import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException; +import org.jivesoftware.smackx.ox.util.PubSubDelegate; +import org.jivesoftware.smackx.pubsub.PubSubException; + +import de.vanitasvitae.crypto.pgpainless.key.UnprotectedKeysProtector; +import org.bouncycastle.openpgp.PGPException; +import org.igniterealtime.smack.inttest.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; +import org.igniterealtime.smack.inttest.TestNotPossibleException; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +public class SecretKeyBackupRestoreIntegrationTest extends AbstractOpenPgpIntegrationTest { + + private static final File beforePath = FileUtils.getTempDir("ox_backup_" + StringUtils.randomString(10)); + private static final File afterPath = FileUtils.getTempDir("ox_restore_" + StringUtils.randomString(10)); + + private String backupCode = null; + + public SecretKeyBackupRestoreIntegrationTest(SmackIntegrationTestEnvironment environment) + throws XMPPException.XMPPErrorException, TestNotPossibleException, SmackException.NotConnectedException, + InterruptedException, SmackException.NoResponseException, SmackException.NotLoggedInException { + super(environment); + if (!OpenPgpManager.serverSupportsSecretKeyBackups(aliceConnection, aliceConnection.getXMPPServiceDomain())) { + throw new TestNotPossibleException("Server does not support the whitelist access model."); + } + } + + @BeforeClass + @AfterClass + public static void cleanUp() { + FileUtils.deleteDirectory(afterPath); + FileUtils.deleteDirectory(beforePath); + } + + @After + @Before + public void deleteBackup() + throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, + SmackException.NoResponseException { + PubSubDelegate.deleteSecretKeyNode(aliceConnection); + } + + @SmackIntegrationTest + public void test() throws SmackOpenPgpException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, + NoSuchProviderException, IOException, InterruptedException, PubSubException.NotALeafNodeException, + SmackException.NoResponseException, SmackException.NotConnectedException, XMPPException.XMPPErrorException, + SmackException.NotLoggedInException, SmackException.FeatureNotSupportedException, + MissingUserIdOnKeyException, NoBackupFoundException, InvalidBackupCodeException, PGPException { + + FileBasedPainlessOpenPgpStore beforeStore = new FileBasedPainlessOpenPgpStore(beforePath, new UnprotectedKeysProtector()); + PainlessOpenPgpProvider beforeProvider = new PainlessOpenPgpProvider(alice, beforeStore); + OpenPgpManager openPgpManager = OpenPgpManager.getInstanceFor(aliceConnection); + openPgpManager.setOpenPgpProvider(beforeProvider); + + beforeStore.setPrimaryOpenPgpKeyPairFingerprint( + openPgpManager.generateAndImportKeyPair(alice)); + + openPgpManager.backupSecretKeyToServer(new DisplayBackupCodeCallback() { + @Override + public void displayBackupCode(String backupCode) { + SecretKeyBackupRestoreIntegrationTest.this.backupCode = backupCode; + } + }, new SecretKeyBackupSelectionCallback() { + @Override + public Set selectKeysToBackup(Set availableSecretKeys) { + return availableSecretKeys; + } + }); + + FileBasedPainlessOpenPgpStore afterStore = new FileBasedPainlessOpenPgpStore(afterPath, new UnprotectedKeysProtector()); + PainlessOpenPgpProvider afterProvider = new PainlessOpenPgpProvider(alice, afterStore); + openPgpManager.setOpenPgpProvider(afterProvider); + + OpenPgpV4Fingerprint fingerprint = openPgpManager.restoreSecretKeyServerBackup(new AskForBackupCodeCallback() { + @Override + public String askForBackupCode() { + return backupCode; + } + }, new SecretKeyRestoreSelectionCallback() { + @Override + public OpenPgpV4Fingerprint selectSecretKeyToRestore(Set availableSecretKeys) { + return availableSecretKeys.iterator().next(); + } + }); + + afterStore.setPrimaryOpenPgpKeyPairFingerprint(fingerprint); + + 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/PainlessOpenPgpProvider.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/PainlessOpenPgpProvider.java index 88b6fb02d..b8eef2ef7 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 @@ -360,6 +360,7 @@ public class PainlessOpenPgpProvider implements OpenPgpProvider { public OpenPgpV4Fingerprint importPublicKey(BareJid owner, byte[] bytes) throws MissingUserIdOnKeyException, IOException, SmackOpenPgpException { PGPPublicKeyRing publicKeys = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + // LOGGER.log(Level.INFO, "Importing key " + Long.toHexString(publicKeys.getPublicKey().getKeyID())); return importPublicKey(owner, publicKeys); } 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 817657fa0..c9b68e057 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 @@ -77,9 +77,11 @@ import org.jivesoftware.smackx.pubsub.ItemsExtension; import org.jivesoftware.smackx.pubsub.LeafNode; import org.jivesoftware.smackx.pubsub.PayloadItem; import org.jivesoftware.smackx.pubsub.PubSubException; +import org.jivesoftware.smackx.pubsub.PubSubFeature; import org.jivesoftware.smackx.pubsub.PubSubManager; import org.jxmpp.jid.BareJid; +import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityBareJid; import org.xmlpull.v1.XmlPullParserException; @@ -257,20 +259,19 @@ public final class OpenPgpManager extends Manager { * * @see XEP-0373 ยง5 * + * @param connection + * @param server Servers {@link DomainBareJid} * @return true, if the server supports secret key backups, otherwise false. * @throws XMPPException.XMPPErrorException * @throws SmackException.NotConnectedException * @throws InterruptedException * @throws SmackException.NoResponseException */ - public boolean serverSupportsSecretKeyBackups() + public static boolean serverSupportsSecretKeyBackups(XMPPConnection connection, DomainBareJid server) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, SmackException.NotLoggedInException { - throwIfNotAuthenticated(); - boolean pep = PEPManager.getInstanceFor(connection()).isSupported(); - boolean whitelist = PubSubManager.getInstance(connection(), connection().getUser().asBareJid()) - .getSupportedFeatures().containsFeature("http://jabber.org/protocol/pubsub#access-whitelist"); - return pep && whitelist; + return PubSubManager.getInstance(connection, server).getSupportedFeatures() + .containsFeature(PubSubFeature.access_whitelist.getFeatureName()); } /** @@ -290,7 +291,8 @@ public final class OpenPgpManager extends Manager { SecretKeyBackupSelectionCallback selectKeyCallback) throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException, - SmackException.NotLoggedInException, SmackOpenPgpException, IOException { + SmackException.NotLoggedInException, SmackOpenPgpException, IOException, + SmackException.FeatureNotSupportedException { throwIfNoProviderSet(); throwIfNotAuthenticated(); @@ -334,7 +336,7 @@ public final class OpenPgpManager extends Manager { * @throws SmackOpenPgpException if something goes wrong while restoring the secret key. * @throws InvalidBackupCodeException if the user-provided backup code is invalid. */ - public void restoreSecretKeyServerBackup(AskForBackupCodeCallback codeCallback, + public OpenPgpV4Fingerprint restoreSecretKeyServerBackup(AskForBackupCodeCallback codeCallback, SecretKeyRestoreSelectionCallback selectionCallback) throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException, SmackOpenPgpException, @@ -350,7 +352,7 @@ public final class OpenPgpManager extends Manager { String backupCode = codeCallback.askForBackupCode(); OpenPgpV4Fingerprint fingerprint = SecretKeyBackupHelper.restoreSecretKeyBackup(provider, backup, backupCode); - provider.getStore().setPrimaryOpenPgpKeyPairFingerprint(fingerprint); + return fingerprint; } /** 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 65fe30ac6..4201257c5 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 @@ -299,7 +299,7 @@ public class PubSubDelegate { throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException, SmackException.NotLoggedInException, SmackException.FeatureNotSupportedException { - if (!OpenPgpManager.getInstanceFor(connection).serverSupportsSecretKeyBackups()) { + if (!OpenPgpManager.serverSupportsSecretKeyBackups(connection, connection.getXMPPServiceDomain())) { throw new SmackException.FeatureNotSupportedException("http://jabber.org/protocol/pubsub#access-whitelist"); } PubSubManager pm = PubSubManager.getInstance(connection);