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 8a3ee85a5..121a1387d 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,8 +16,6 @@ */ package org.jivesoftware.smackx.openpgp; -import static junit.framework.TestCase.assertTrue; - import java.io.File; import java.util.logging.Level; @@ -29,11 +27,10 @@ import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.ox.OXInstantMessagingManager; import org.jivesoftware.smackx.ox.OpenPgpContact; 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.crypto.PainlessOpenPgpProvider; import org.jivesoftware.smackx.ox.element.SigncryptElement; import org.jivesoftware.smackx.ox.listener.OxMessageListener; +import org.jivesoftware.smackx.ox.store.filebased.FileBasedOpenPgpStore; import org.jivesoftware.smackx.ox.util.PubSubDelegate; import org.igniterealtime.smack.inttest.SmackIntegrationTest; @@ -43,6 +40,7 @@ import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; +import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.pgpainless.key.protection.UnprotectedKeysProtector; public class BasicOpenPgpInstantMessagingIntegrationTest extends AbstractOpenPgpIntegrationTest { @@ -75,11 +73,13 @@ 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!!!"; - FileBasedPainlessOpenPgpStore aliceStore = new FileBasedPainlessOpenPgpStore(aliceStorePath, new UnprotectedKeysProtector()); - FileBasedPainlessOpenPgpStore bobStore = new FileBasedPainlessOpenPgpStore(bobStorePath, new UnprotectedKeysProtector()); + FileBasedOpenPgpStore aliceStore = new FileBasedOpenPgpStore(aliceStorePath); + aliceStore.setKeyRingProtector(new UnprotectedKeysProtector()); + FileBasedOpenPgpStore bobStore = new FileBasedOpenPgpStore(bobStorePath); + bobStore.setKeyRingProtector(new UnprotectedKeysProtector()); - PainlessOpenPgpProvider aliceProvider = new PainlessOpenPgpProvider(alice, aliceStore); - PainlessOpenPgpProvider bobProvider = new PainlessOpenPgpProvider(bob, bobStore); + PainlessOpenPgpProvider aliceProvider = new PainlessOpenPgpProvider(aliceStore); + PainlessOpenPgpProvider bobProvider = new PainlessOpenPgpProvider(bobStore); OpenPgpManager aliceOpenPgp = OpenPgpManager.getInstanceFor(aliceConnection); OpenPgpManager bobOpenPgp = OpenPgpManager.getInstanceFor(bobConnection); @@ -104,20 +104,13 @@ public class BasicOpenPgpInstantMessagingIntegrationTest extends AbstractOpenPgp aliceFingerprint = aliceOpenPgp.generateAndImportKeyPair(alice); bobFingerprint = bobOpenPgp.generateAndImportKeyPair(bob); - aliceStore.setSigningKeyPairFingerprint(aliceFingerprint); - bobStore.setSigningKeyPairFingerprint(bobFingerprint); - aliceOpenPgp.announceSupportAndPublish(); bobOpenPgp.announceSupportAndPublish(); OpenPgpContact bobForAlice = aliceOpenPgp.getOpenPgpContact(bob.asEntityBareJidIfPossible()); OpenPgpContact aliceForBob = bobOpenPgp.getOpenPgpContact(alice.asEntityBareJidIfPossible()); - bobForAlice.updateKeys(); - aliceForBob.updateKeys(); - - assertTrue(bobForAlice.getActiveKeys().contains(bobFingerprint)); - assertTrue(aliceForBob.getActiveKeys().contains(aliceFingerprint)); + // TODO: Update keys 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 index a01bebd36..c878e007d 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 @@ -18,6 +18,7 @@ package org.jivesoftware.smackx.openpgp; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertNull; import static junit.framework.TestCase.assertTrue; @@ -34,21 +35,23 @@ 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.OpenPgpSelf; import org.jivesoftware.smackx.ox.callback.backup.AskForBackupCodeCallback; import org.jivesoftware.smackx.ox.callback.backup.DisplayBackupCodeCallback; import org.jivesoftware.smackx.ox.callback.backup.SecretKeyBackupSelectionCallback; import org.jivesoftware.smackx.ox.callback.backup.SecretKeyRestoreSelectionCallback; +import org.jivesoftware.smackx.ox.crypto.PainlessOpenPgpProvider; import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException; +import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException; 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.store.definition.OpenPgpStore; +import org.jivesoftware.smackx.ox.store.filebased.FileBasedOpenPgpStore; import org.jivesoftware.smackx.ox.util.PubSubDelegate; import org.jivesoftware.smackx.pubsub.PubSubException; import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.igniterealtime.smack.inttest.SmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; @@ -56,6 +59,7 @@ import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.pgpainless.key.protection.UnprotectedKeysProtector; public class SecretKeyBackupRestoreIntegrationTest extends AbstractOpenPgpIntegrationTest { @@ -90,24 +94,29 @@ public class SecretKeyBackupRestoreIntegrationTest extends AbstractOpenPgpIntegr } @SmackIntegrationTest - public void test() throws SmackOpenPgpException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, + public void test() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException, IOException, InterruptedException, PubSubException.NotALeafNodeException, SmackException.NoResponseException, SmackException.NotConnectedException, XMPPException.XMPPErrorException, SmackException.NotLoggedInException, SmackException.FeatureNotSupportedException, - MissingUserIdOnKeyException, NoBackupFoundException, InvalidBackupCodeException, PGPException { + MissingUserIdOnKeyException, NoBackupFoundException, InvalidBackupCodeException, PGPException, MissingOpenPgpKeyPairException { - FileBasedPainlessOpenPgpStore beforeStore = new FileBasedPainlessOpenPgpStore(beforePath, new UnprotectedKeysProtector()); - PainlessOpenPgpProvider beforeProvider = new PainlessOpenPgpProvider(alice, beforeStore); + OpenPgpStore beforeStore = new FileBasedOpenPgpStore(beforePath); + beforeStore.setKeyRingProtector(new UnprotectedKeysProtector()); + PainlessOpenPgpProvider beforeProvider = new PainlessOpenPgpProvider(beforeStore); OpenPgpManager openPgpManager = OpenPgpManager.getInstanceFor(aliceConnection); openPgpManager.setOpenPgpProvider(beforeProvider); - assertNull(beforeStore.getSigningKeyPairFingerprint()); + OpenPgpSelf self = openPgpManager.getOpenPgpSelf(); + + assertNull(self.getSigningKeyFingerprint()); OpenPgpV4Fingerprint keyFingerprint = openPgpManager.generateAndImportKeyPair(alice); - beforeStore.setSigningKeyPairFingerprint(keyFingerprint); - assertEquals(keyFingerprint, beforeStore.getSigningKeyPairFingerprint()); + assertEquals(keyFingerprint, self.getSigningKeyFingerprint()); - assertTrue(beforeStore.getAvailableKeyPairFingerprints(alice).contains(keyFingerprint)); + assertTrue(self.getSecretKeys().contains(keyFingerprint.getKeyId())); + + PGPSecretKeyRing beforeKeys = beforeStore.getSecretKeyRing(alice, keyFingerprint); + assertNotNull(beforeKeys); openPgpManager.backupSecretKeyToServer(new DisplayBackupCodeCallback() { @Override @@ -121,12 +130,15 @@ public class SecretKeyBackupRestoreIntegrationTest extends AbstractOpenPgpIntegr } }); - FileBasedPainlessOpenPgpStore afterStore = new FileBasedPainlessOpenPgpStore(afterPath, new UnprotectedKeysProtector()); - PainlessOpenPgpProvider afterProvider = new PainlessOpenPgpProvider(alice, afterStore); + FileBasedOpenPgpStore afterStore = new FileBasedOpenPgpStore(afterPath); + afterStore.setKeyRingProtector(new UnprotectedKeysProtector()); + PainlessOpenPgpProvider afterProvider = new PainlessOpenPgpProvider(afterStore); openPgpManager.setOpenPgpProvider(afterProvider); - assertNull(afterStore.getSigningKeyPairFingerprint()); - assertFalse(afterStore.getAvailableKeyPairFingerprints(alice).contains(keyFingerprint)); + self = openPgpManager.getOpenPgpSelf(); + + assertNull(self.getSigningKeyFingerprint()); + assertFalse(self.getSecretKeys().contains(keyFingerprint.getKeyId())); OpenPgpV4Fingerprint fingerprint = openPgpManager.restoreSecretKeyServerBackup(new AskForBackupCodeCallback() { @Override @@ -140,11 +152,12 @@ public class SecretKeyBackupRestoreIntegrationTest extends AbstractOpenPgpIntegr } }); - assertTrue(afterStore.getAvailableKeyPairFingerprints(alice).contains(keyFingerprint)); + assertTrue(self.getSecretKeys().contains(keyFingerprint.getKeyId())); - afterStore.setSigningKeyPairFingerprint(fingerprint); + assertEquals(keyFingerprint, self.getSigningKeyFingerprint()); - assertEquals(keyFingerprint, afterStore.getSigningKeyPairFingerprint()); - assertTrue(Arrays.equals(beforeStore.getSecretKeyRings(alice).getEncoded(), afterStore.getSecretKeyRings(alice).getEncoded())); + PGPSecretKeyRing afterKeys = afterStore.getSecretKeyRing(alice, keyFingerprint); + assertNotNull(afterKeys); + assertTrue(Arrays.equals(beforeKeys.getEncoded(), afterKeys.getEncoded())); } } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OXInstantMessagingManager.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OXInstantMessagingManager.java index 1bdbe1d4d..89cb188d1 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OXInstantMessagingManager.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OXInstantMessagingManager.java @@ -18,6 +18,7 @@ package org.jivesoftware.smackx.ox; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -34,13 +35,14 @@ import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement; import org.jivesoftware.smackx.hints.element.StoreHint; +import org.jivesoftware.smackx.ox.element.OpenPgpElement; import org.jivesoftware.smackx.ox.element.SigncryptElement; -import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException; -import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException; import org.jivesoftware.smackx.ox.listener.OxMessageListener; import org.jivesoftware.smackx.ox.listener.internal.SigncryptElementReceivedListener; +import org.bouncycastle.openpgp.PGPException; import org.jxmpp.jid.BareJid; +import org.jxmpp.jid.Jid; /** * Entry point of Smacks API for XEP-0374: OpenPGP for XMPP: Instant Messaging. @@ -55,10 +57,12 @@ public final class OXInstantMessagingManager extends Manager implements Signcryp private static final Map INSTANCES = new WeakHashMap<>(); private final Set oxMessageListeners = new HashSet<>(); + private final OpenPgpManager openPgpManager; private OXInstantMessagingManager(final XMPPConnection connection) { super(connection); - OpenPgpManager.getInstanceFor(connection).registerSigncryptReceivedListener(this); + openPgpManager = OpenPgpManager.getInstanceFor(connection); + openPgpManager.registerSigncryptReceivedListener(this); announceSupportForOxInstantMessaging(); } @@ -153,15 +157,13 @@ public final class OXInstantMessagingManager extends Manager implements Signcryp * @param contact contact capable of OpenPGP for XMPP: Instant Messaging. * @param body message body. * @throws InterruptedException if the thread is interrupted - * @throws MissingOpenPgpKeyPairException if we cannot access our signing key * @throws IOException IO is dangerous * @throws SmackException.NotConnectedException if we are not connected - * @throws SmackOpenPgpException in case of an OpenPGP error * @throws SmackException.NotLoggedInException if we are not logged in */ public void sendOxMessage(OpenPgpContact contact, CharSequence body) - throws InterruptedException, MissingOpenPgpKeyPairException, IOException, - SmackException.NotConnectedException, SmackOpenPgpException, SmackException.NotLoggedInException { + throws InterruptedException, IOException, + SmackException.NotConnectedException, SmackException.NotLoggedInException, PGPException { Message message = new Message(contact.getJid()); List payload = new ArrayList<>(); payload.add(new Message.Body(null, body.toString())); @@ -172,7 +174,12 @@ public final class OXInstantMessagingManager extends Manager implements Signcryp StoreHint.set(message); message.setBody("This message is encrypted using XEP-0374: OpenPGP for XMPP: Instant Messaging."); - //contact.addSignedEncryptedPayloadTo(message, payload); + SigncryptElement signcryptElement = new SigncryptElement(Collections.singleton(contact.jid), payload); + + OpenPgpElement encrypted = openPgpManager.getProvider().signAndEncrypt(signcryptElement, + openPgpManager.getOpenPgpSelf(), Collections.singleton(contact)); + + message.addExtension(encrypted); ChatManager.getInstanceFor(connection()).chatWith(contact.getJid().asEntityBareJidIfPossible()).send(message); } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpContact.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpContact.java index 8f27a154e..d1c505496 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpContact.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpContact.java @@ -1,9 +1,29 @@ +/** + * + * Copyright 2018 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.ox; import java.io.IOException; -import java.util.Set; +import java.util.Date; +import java.util.Map; +import java.util.logging.Level; import java.util.logging.Logger; +import org.jivesoftware.smackx.ox.selection_strategy.AnnouncedKeys; +import org.jivesoftware.smackx.ox.selection_strategy.BareJidUserId; import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore; import org.bouncycastle.openpgp.PGPException; @@ -35,14 +55,25 @@ public class OpenPgpContact { public PGPPublicKeyRingCollection getAnnouncedPublicKeys() throws IOException, PGPException { PGPPublicKeyRingCollection anyKeys = getAnyPublicKeys(); - Set announced = store.getAnnouncedFingerprintsOf(jid).keySet(); + Map announced = store.getAnnouncedFingerprintsOf(jid); PGPPublicKeyRingCollection announcedKeysCollection = anyKeys; + + BareJidUserId.PubRingSelectionStrategy userIdFilter = new BareJidUserId.PubRingSelectionStrategy(); + AnnouncedKeys.PubKeyRingSelectionStrategy announcedFilter = new AnnouncedKeys.PubKeyRingSelectionStrategy(); + for (PGPPublicKeyRing ring : anyKeys) { - OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(ring.getPublicKey()); + if (!userIdFilter.accept(getJid(), ring)) { + LOGGER.log(Level.WARNING, "Ignore key " + Long.toHexString(ring.getPublicKey().getKeyID()) + + " as it lacks the user-id \"xmpp" + getJid().toString() + "\""); + announcedKeysCollection = PGPPublicKeyRingCollection.removePublicKeyRing(announcedKeysCollection, ring); + continue; + } - if (!announced.contains(fingerprint)) { + if (!announcedFilter.accept(announced, ring)) { + LOGGER.log(Level.WARNING, "Ignore key " + Long.toHexString(ring.getPublicKey().getKeyID()) + + " as it is not announced by " + getJid().toString()); announcedKeysCollection = PGPPublicKeyRingCollection.removePublicKeyRing(announcedKeysCollection, ring); } } 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 b15693e76..faccd3d65 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 @@ -18,6 +18,7 @@ package org.jivesoftware.smackx.ox; import static org.jivesoftware.smackx.ox.util.PubSubDelegate.PEP_NODE_PUBLIC_KEYS; import static org.jivesoftware.smackx.ox.util.PubSubDelegate.PEP_NODE_PUBLIC_KEYS_NOTIFY; +import static org.jivesoftware.smackx.ox.util.PubSubDelegate.publishPublicKey; import java.io.IOException; import java.io.InputStream; @@ -50,16 +51,18 @@ import org.jivesoftware.smackx.ox.callback.backup.SecretKeyBackupSelectionCallba import org.jivesoftware.smackx.ox.callback.backup.SecretKeyRestoreSelectionCallback; import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider; import org.jivesoftware.smackx.ox.element.CryptElement; +import org.jivesoftware.smackx.ox.element.OpenPgpContentElement; +import org.jivesoftware.smackx.ox.element.OpenPgpElement; import org.jivesoftware.smackx.ox.element.PubkeyElement; import org.jivesoftware.smackx.ox.element.PublicKeysListElement; import org.jivesoftware.smackx.ox.element.SecretkeyElement; import org.jivesoftware.smackx.ox.element.SignElement; import org.jivesoftware.smackx.ox.element.SigncryptElement; import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException; +import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException; import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException; 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.listener.internal.CryptElementReceivedListener; import org.jivesoftware.smackx.ox.listener.internal.SignElementReceivedListener; import org.jivesoftware.smackx.ox.listener.internal.SigncryptElementReceivedListener; @@ -75,10 +78,15 @@ import org.jivesoftware.smackx.pubsub.PubSubException; import org.jivesoftware.smackx.pubsub.PubSubFeature; import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.jxmpp.jid.BareJid; import org.jxmpp.jid.EntityBareJid; +import org.pgpainless.pgpainless.PGPainless; import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint; +import org.xmlpull.v1.XmlPullParserException; public final class OpenPgpManager extends Manager { @@ -94,8 +102,6 @@ public final class OpenPgpManager extends Manager { */ private OpenPgpProvider provider; - private OpenPgpStore store; - private final Map openPgpCapableContacts = new HashMap<>(); private final Set signcryptElementReceivedListeners = new HashSet<>(); @@ -144,6 +150,10 @@ public final class OpenPgpManager extends Manager { this.provider = provider; } + OpenPgpProvider getProvider() { + return provider; + } + /** * Get our OpenPGP self. * @@ -151,8 +161,10 @@ public final class OpenPgpManager extends Manager { * @throws SmackException.NotLoggedInException if we are not logged in */ public OpenPgpSelf getOpenPgpSelf() throws SmackException.NotLoggedInException { + throwIfNoProviderSet(); + if (self == null) { - self = new OpenPgpSelf(getJidOrThrow(), store); + self = new OpenPgpSelf(getJidOrThrow(), provider.getStore()); } return self; @@ -165,7 +177,6 @@ public final class OpenPgpManager extends Manager { * * @throws NoSuchAlgorithmException if we are missing an algorithm to generate a fresh key pair. * @throws NoSuchProviderException if we are missing a suitable {@link java.security.Provider}. - * @throws SmackOpenPgpException if something bad happens during key generation/loading. * @throws InterruptedException if the thread gets interrupted. * @throws PubSubException.NotALeafNodeException if one of the PubSub nodes is not a {@link LeafNode}. * @throws XMPPException.XMPPErrorException in case of an XMPP protocol error. @@ -174,33 +185,32 @@ public final class OpenPgpManager extends Manager { * @throws IOException IO is dangerous. * @throws InvalidAlgorithmParameterException if illegal algorithm parameters are used for key generation. * @throws SmackException.NotLoggedInException if we are not logged in. + * @throws PGPException if something goes wrong during key loading/generating */ public void announceSupportAndPublish() - throws NoSuchAlgorithmException, NoSuchProviderException, SmackOpenPgpException, - InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, + throws NoSuchAlgorithmException, NoSuchProviderException, InterruptedException, + PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException, IOException, - InvalidAlgorithmParameterException, SmackException.NotLoggedInException { + InvalidAlgorithmParameterException, SmackException.NotLoggedInException, PGPException { throwIfNoProviderSet(); throwIfNotAuthenticated(); - BareJid ourJid = connection().getUser().asBareJid(); + OpenPgpV4Fingerprint primaryFingerprint = getOurFingerprint(); - // OpenPgpV4Fingerprint primaryFingerprint = getOurFingerprint(); - - // if (primaryFingerprint == null) { - // primaryFingerprint = generateAndImportKeyPair(ourJid); - // } + if (primaryFingerprint == null) { + primaryFingerprint = generateAndImportKeyPair(getJidOrThrow()); + } // Create element PubkeyElement pubkeyElement; - // try { - // pubkeyElement = createPubkeyElement(ourJid, primaryFingerprint, new Date()); - // } catch (MissingOpenPgpPublicKeyException e) { - // throw new AssertionError("Cannot publish our public key, since it is missing (MUST NOT happen!)"); - // } + try { + pubkeyElement = createPubkeyElement(getJidOrThrow(), primaryFingerprint, new Date()); + } catch (MissingOpenPgpPublicKeyException e) { + throw new AssertionError("Cannot publish our public key, since it is missing (MUST NOT happen!)"); + } // publish it - // publishPublicKey(connection(), pubkeyElement, primaryFingerprint); + publishPublicKey(connection(), pubkeyElement, primaryFingerprint); // Subscribe to public key changes // PEPManager.getInstanceFor(connection()).addPEPListener(metadataListener); @@ -216,19 +226,20 @@ public final class OpenPgpManager extends Manager { * @throws NoSuchAlgorithmException if the JVM doesn't support one of the used algorithms. * @throws InvalidAlgorithmParameterException if the used algorithm parameters are invalid. * @throws NoSuchProviderException if we are missing a cryptographic provider. - * @throws SmackOpenPgpException in case of an OpenPGP error. */ public OpenPgpV4Fingerprint generateAndImportKeyPair(BareJid ourJid) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException, - SmackOpenPgpException, PGPException { + PGPException, IOException { + throwIfNoProviderSet(); + OpenPgpStore store = provider.getStore(); PGPSecretKeyRing keyPair = store.generateKeyRing(ourJid); - // try { - // provider.importSecretKey(ourJid, keyPair); - // } catch (MissingUserIdOnKeyException e) { + try { + store.importSecretKey(ourJid, keyPair); + } catch (MissingUserIdOnKeyException e) { // This should never throw, since we set our jid literally one line above this comment. - // throw new AssertionError(e); - // } + throw new AssertionError(e); + } return new OpenPgpV4Fingerprint(keyPair); } @@ -251,7 +262,8 @@ public final class OpenPgpManager extends Manager { * @return {@link OpenPgpContact}. */ public OpenPgpContact getOpenPgpContact(EntityBareJid jid) { - return store.getOpenPgpContact(jid); + throwIfNoProviderSet(); + return provider.getStore().getOpenPgpContact(jid); } @@ -288,7 +300,6 @@ public final class OpenPgpManager extends Manager { * @throws SmackException.NotConnectedException if we are not connected. * @throws SmackException.NoResponseException if the server doesn't respond. * @throws SmackException.NotLoggedInException if we are not logged in. - * @throws SmackOpenPgpException in case of an OpenPGP exception. * @throws IOException IO is dangerous. * @throws SmackException.FeatureNotSupportedException if the server doesn't support the PubSub whitelist access model. */ @@ -296,21 +307,28 @@ public final class OpenPgpManager extends Manager { SecretKeyBackupSelectionCallback selectKeyCallback) throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException, - SmackException.NotLoggedInException, SmackOpenPgpException, IOException, - SmackException.FeatureNotSupportedException { + SmackException.NotLoggedInException, IOException, + SmackException.FeatureNotSupportedException, PGPException, MissingOpenPgpKeyPairException { throwIfNoProviderSet(); throwIfNotAuthenticated(); BareJid ownJid = connection().getUser().asBareJid(); String backupCode = SecretKeyBackupHelper.generateBackupPassword(); - // Set availableKeyPairs = provider.getStore().getAvailableKeyPairFingerprints(ownJid); - // Set selectedKeyPairs = selectKeyCallback.selectKeysToBackup(availableKeyPairs); - // SecretkeyElement secretKey = SecretKeyBackupHelper.createSecretkeyElement(provider, ownJid, selectedKeyPairs, backupCode); + PGPSecretKeyRingCollection secretKeyRings = provider.getStore().getSecretKeysOf(ownJid); - // PubSubDelegate.depositSecretKey(connection(), secretKey); - // displayCodeCallback.displayBackupCode(backupCode); + Set availableKeyPairs = new HashSet<>(); + for (PGPSecretKeyRing ring : secretKeyRings) { + availableKeyPairs.add(new OpenPgpV4Fingerprint(ring)); + } + + Set selectedKeyPairs = selectKeyCallback.selectKeysToBackup(availableKeyPairs); + + SecretkeyElement secretKey = SecretKeyBackupHelper.createSecretkeyElement(provider, ownJid, selectedKeyPairs, backupCode); + + PubSubDelegate.depositSecretKey(connection(), secretKey); + displayCodeCallback.displayBackupCode(backupCode); } /** @@ -342,7 +360,6 @@ public final class OpenPgpManager extends Manager { * @throws XMPPException.XMPPErrorException in case of an XMPP protocol error. * @throws SmackException.NotConnectedException if we are not connected. * @throws SmackException.NoResponseException if the server doesn't respond. - * @throws SmackOpenPgpException if something goes wrong while restoring the secret key. * @throws InvalidBackupCodeException if the user-provided backup code is invalid. * @throws SmackException.NotLoggedInException if we are not logged in * @throws IOException IO is dangerous @@ -350,11 +367,11 @@ public final class OpenPgpManager extends Manager { * @throws NoBackupFoundException if no secret key backup has been found */ public OpenPgpV4Fingerprint restoreSecretKeyServerBackup(AskForBackupCodeCallback codeCallback, - SecretKeyRestoreSelectionCallback selectionCallback) + SecretKeyRestoreSelectionCallback selectionCallback) throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, SmackException.NoResponseException, SmackOpenPgpException, + SmackException.NotConnectedException, SmackException.NoResponseException, InvalidBackupCodeException, SmackException.NotLoggedInException, IOException, MissingUserIdOnKeyException, - NoBackupFoundException { + NoBackupFoundException, PGPException { throwIfNoProviderSet(); throwIfNotAuthenticated(); SecretkeyElement backup = PubSubDelegate.fetchSecretKey(connection()); @@ -364,8 +381,9 @@ public final class OpenPgpManager extends Manager { String backupCode = codeCallback.askForBackupCode(); - OpenPgpV4Fingerprint fingerprint = SecretKeyBackupHelper.restoreSecretKeyBackup(provider, backup, backupCode); - return fingerprint; + PGPSecretKeyRing key = SecretKeyBackupHelper.restoreSecretKeyBackup(backup, backupCode); + provider.getStore().importSecretKey(getJidOrThrow(), key); + return new OpenPgpV4Fingerprint(key); } /** @@ -394,21 +412,24 @@ public final class OpenPgpManager extends Manager { }; private void processPublicKeysListElement(BareJid contact, PublicKeysListElement listElement) { - /* - OpenPgpContact openPgpContact = getOpenPgpContact(contact.asEntityBareJidIfPossible()); + try { - openPgpContact.updateKeys(listElement); - } catch (SmackOpenPgpException e) { - LOGGER.log(Level.WARNING, "Could not read key ring of contact " + contact, e); + PGPPublicKeyRingCollection contactsKeys = provider.getStore().getPublicKeysOf(contact); + for (OpenPgpV4Fingerprint fingerprint : listElement.getMetadata().keySet()) { + PGPPublicKeyRing key = contactsKeys.getPublicKeyRing(fingerprint.getKeyId()); + if (key == null) { + + } + } + } catch (PGPException | IOException e) { + LOGGER.log(Level.WARNING, "Could not read public keys of " + contact, e); } - */ } private final IncomingChatMessageListener incomingOpenPgpMessageListener = new IncomingChatMessageListener() { @Override public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) { - /* OpenPgpElement element = message.getExtension(OpenPgpElement.ELEMENT, OpenPgpElement.NAMESPACE); if (element == null) { // Message does not contain an OpenPgpElement -> discard @@ -419,13 +440,11 @@ public final class OpenPgpManager extends Manager { OpenPgpContentElement contentElement = null; try { - contentElement = contact.receive(element); - } catch (SmackOpenPgpException e) { + contentElement = provider.decryptAndOrVerify(element, self, contact).getOpenPgpContentElement(); + } catch (PGPException e) { LOGGER.log(Level.WARNING, "Could not decrypt incoming OpenPGP encrypted message", e); } catch (XmlPullParserException | IOException e) { LOGGER.log(Level.WARNING, "Invalid XML content of incoming OpenPGP encrypted message", e); - } catch (MissingOpenPgpKeyPairException e) { - LOGGER.log(Level.WARNING, "Could not decrypt incoming OpenPGP encrypted message due to missing secret key", e); } if (contentElement instanceof SigncryptElement) { @@ -452,7 +471,6 @@ public final class OpenPgpManager extends Manager { else { throw new AssertionError("Invalid element received: " + contentElement.getClass().getName()); } - */ } }; @@ -468,12 +486,12 @@ public final class OpenPgpManager extends Manager { * @throws MissingUserIdOnKeyException if the key does not have an OpenPGP user-id of the form * "xmpp:juliet@capulet.lit" with the owners {@link BareJid} * @throws IOException row, row, row your byte gently down the {@link InputStream} - * @throws SmackOpenPgpException if the key cannot be deserialized */ private void processPublicKey(PubkeyElement pubkeyElement, BareJid owner) - throws MissingUserIdOnKeyException, IOException, SmackOpenPgpException { + throws MissingUserIdOnKeyException, IOException, PGPException { byte[] base64 = pubkeyElement.getDataElement().getB64Data(); - // provider.importPublicKey(owner, Base64.decode(base64)); + PGPPublicKeyRing keyRing = PGPainless.readKeyRing().publicKeyRing(Base64.decode(base64)); + provider.getStore().importPublicKey(owner, keyRing); } /** @@ -490,10 +508,16 @@ public final class OpenPgpManager extends Manager { private PubkeyElement createPubkeyElement(BareJid owner, OpenPgpV4Fingerprint fingerprint, Date date) - throws MissingOpenPgpPublicKeyException { - // byte[] keyBytes = provider.getStore().getPublicKeyRingBytes(owner, fingerprint); - // return createPubkeyElement(keyBytes, date); - return null; + throws MissingOpenPgpPublicKeyException, IOException, PGPException { + PGPPublicKeyRingCollection publicKeyRingCollection = provider.getStore().getPublicKeysOf(owner); + if (publicKeyRingCollection != null) { + PGPPublicKeyRing keys = publicKeyRingCollection.getPublicKeyRing(fingerprint.getKeyId()); + if (keys != null) { + byte[] keyBytes = keys.getEncoded(true); + return createPubkeyElement(keyBytes, date); + } + } + throw new MissingOpenPgpPublicKeyException(owner, fingerprint); } /** diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpSelf.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpSelf.java index ad5502c37..419a2ccd2 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpSelf.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpSelf.java @@ -1,3 +1,19 @@ +/** + * + * Copyright 2018 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.ox; import java.io.IOException; diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/SecretKeyPassphraseCallback.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/SecretKeyPassphraseCallback.java index a94db2fac..ef44bf8f1 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/SecretKeyPassphraseCallback.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/SecretKeyPassphraseCallback.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/OpenPgpProvider.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/OpenPgpProvider.java index 2a96278e5..a12df4903 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/OpenPgpProvider.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/OpenPgpProvider.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,11 +26,14 @@ import org.jivesoftware.smackx.ox.element.CryptElement; import org.jivesoftware.smackx.ox.element.OpenPgpElement; import org.jivesoftware.smackx.ox.element.SignElement; import org.jivesoftware.smackx.ox.element.SigncryptElement; +import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore; import org.bouncycastle.openpgp.PGPException; public interface OpenPgpProvider { + OpenPgpStore getStore(); + OpenPgpElement signAndEncrypt(SigncryptElement element, OpenPgpSelf self, Collection recipients) throws IOException, PGPException; diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java index 4cf0263eb..89e024300 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java @@ -1,3 +1,19 @@ +/** + * + * Copyright 2018 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.ox.crypto; import java.io.ByteArrayOutputStream; @@ -14,6 +30,7 @@ import org.jivesoftware.smackx.ox.element.CryptElement; import org.jivesoftware.smackx.ox.element.OpenPgpElement; import org.jivesoftware.smackx.ox.element.SignElement; import org.jivesoftware.smackx.ox.element.SigncryptElement; +import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; @@ -25,6 +42,17 @@ import org.pgpainless.pgpainless.encryption_signing.EncryptionStream; public class PainlessOpenPgpProvider implements OpenPgpProvider { + private final OpenPgpStore store; + + public PainlessOpenPgpProvider(OpenPgpStore store) { + this.store = store; + } + + @Override + public OpenPgpStore getStore() { + return store; + } + @Override public OpenPgpElement signAndEncrypt(SigncryptElement element, OpenPgpSelf self, Collection recipients) throws IOException, PGPException { diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/SmackOpenPgpException.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/SmackOpenPgpException.java deleted file mode 100644 index 274f1910d..000000000 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/SmackOpenPgpException.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * - * Copyright 2018 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.ox.exception; - -public class SmackOpenPgpException extends Exception { - - private static final long serialVersionUID = 1L; - - public SmackOpenPgpException(Throwable cause) { - super(cause); - } - - public SmackOpenPgpException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/selection_strategy/AnnouncedKeys.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/selection_strategy/AnnouncedKeys.java new file mode 100644 index 000000000..9c405b3ef --- /dev/null +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/selection_strategy/AnnouncedKeys.java @@ -0,0 +1,62 @@ +/** + * + * Copyright 2018 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.ox.selection_strategy; + +import java.util.Date; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint; +import org.pgpainless.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy; +import org.pgpainless.pgpainless.key.selection.keyring.SecretKeyRingSelectionStrategy; + +public class AnnouncedKeys { + + public static class PubKeyRingSelectionStrategy extends PublicKeyRingSelectionStrategy> { + + private static final Logger LOGGER = Logger.getLogger(PubKeyRingSelectionStrategy.class.getName()); + + @Override + public boolean accept(Map announcedKeys, PGPPublicKeyRing publicKeys) { + try { + return announcedKeys.keySet().contains(new OpenPgpV4Fingerprint(publicKeys)); + } catch (PGPException e) { + LOGGER.log(Level.WARNING, "Key might be damaged.", e); + return false; + } + } + } + + public static class SecKeyRingSelectionStrategy extends SecretKeyRingSelectionStrategy> { + + private static final Logger LOGGER = Logger.getLogger(SecKeyRingSelectionStrategy.class.getName()); + + @Override + public boolean accept(Map announcedKeys, PGPSecretKeyRing secretKeys) { + try { + return announcedKeys.keySet().contains(new OpenPgpV4Fingerprint(secretKeys)); + } catch (PGPException e) { + LOGGER.log(Level.WARNING, "Key might be damaged.", e); + return false; + } + } + } +} diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpKeyStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpKeyStore.java index c045c9eaa..b871fffff 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpKeyStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpKeyStore.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,11 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException; +import org.jivesoftware.smackx.ox.selection_strategy.BareJidUserId; import org.jivesoftware.smackx.ox.store.definition.OpenPgpKeyStore; import org.bouncycastle.openpgp.PGPException; @@ -37,12 +41,10 @@ import org.pgpainless.pgpainless.key.generation.type.length.RsaLength; public abstract class AbstractOpenPgpKeyStore implements OpenPgpKeyStore { - public AbstractOpenPgpKeyStore() { + private static final Logger LOGGER = Logger.getLogger(AbstractOpenPgpKeyStore.class.getName()); - } - - protected Map publicKeys = new HashMap<>(); - protected Map secretKeys = new HashMap<>(); + protected Map publicKeyRingCollections = new HashMap<>(); + protected Map secretKeyRingCollections = new HashMap<>(); protected abstract PGPPublicKeyRingCollection readPublicKeysOf(BareJid owner) throws IOException, PGPException; @@ -54,11 +56,11 @@ public abstract class AbstractOpenPgpKeyStore implements OpenPgpKeyStore { @Override public PGPPublicKeyRingCollection getPublicKeysOf(BareJid owner) throws IOException, PGPException { - PGPPublicKeyRingCollection keys = publicKeys.get(owner); + PGPPublicKeyRingCollection keys = publicKeyRingCollections.get(owner); if (keys == null) { keys = readPublicKeysOf(owner); if (keys != null) { - publicKeys.put(owner, keys); + publicKeyRingCollections.put(owner, keys); } } return keys; @@ -66,16 +68,53 @@ public abstract class AbstractOpenPgpKeyStore implements OpenPgpKeyStore { @Override public PGPSecretKeyRingCollection getSecretKeysOf(BareJid owner) throws IOException, PGPException { - PGPSecretKeyRingCollection keys = secretKeys.get(owner); + PGPSecretKeyRingCollection keys = secretKeyRingCollections.get(owner); if (keys == null) { keys = readSecretKeysOf(owner); if (keys != null) { - secretKeys.put(owner, keys); + secretKeyRingCollections.put(owner, keys); } } return keys; } + @Override + public void importSecretKey(BareJid owner, PGPSecretKeyRing secretKeys) + throws IOException, PGPException, MissingUserIdOnKeyException { + + if (!new BareJidUserId.SecRingSelectionStrategy().accept(owner, secretKeys)) { + throw new MissingUserIdOnKeyException(owner, secretKeys.getPublicKey().getKeyID()); + } + + PGPSecretKeyRingCollection secretKeyRings = getSecretKeysOf(owner); + try { + secretKeyRings = PGPSecretKeyRingCollection.addSecretKeyRing(secretKeyRings, secretKeys); + } catch (IllegalArgumentException e) { + LOGGER.log(Level.INFO, "Skipping secret key ring " + Long.toHexString(secretKeys.getPublicKey().getKeyID()) + + " as it is already in the key ring of " + owner.toString()); + } + this.secretKeyRingCollections.put(owner, secretKeyRings); + writeSecretKeysOf(owner, secretKeyRings); + } + + @Override + public void importPublicKey(BareJid owner, PGPPublicKeyRing publicKeys) throws IOException, PGPException, MissingUserIdOnKeyException { + + if (!new BareJidUserId.PubRingSelectionStrategy().accept(owner, publicKeys)) { + throw new MissingUserIdOnKeyException(owner, publicKeys.getPublicKey().getKeyID()); + } + + PGPPublicKeyRingCollection publicKeyRings = getPublicKeysOf(owner); + try { + publicKeyRings = PGPPublicKeyRingCollection.addPublicKeyRing(publicKeyRings, publicKeys); + } catch (IllegalArgumentException e) { + LOGGER.log(Level.INFO, "Skipping public key ring " + Long.toHexString(publicKeys.getPublicKey().getKeyID()) + + " as it is already in the key ring of " + owner.toString()); + } + this.publicKeyRingCollections.put(owner, publicKeyRings); + writePublicKeysOf(owner, publicKeyRings); + } + @Override public PGPPublicKeyRing getPublicKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { PGPPublicKeyRingCollection publicKeyRings = getPublicKeysOf(owner); diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpMetadataStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpMetadataStore.java index 7a877002a..8c18ff0eb 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpMetadataStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpMetadataStore.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpStore.java index a7e1299ff..f00bd4196 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpStore.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import java.util.Observable; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smackx.ox.OpenPgpContact; import org.jivesoftware.smackx.ox.callback.SecretKeyPassphraseCallback; +import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException; import org.jivesoftware.smackx.ox.store.definition.OpenPgpKeyStore; import org.jivesoftware.smackx.ox.store.definition.OpenPgpMetadataStore; import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore; @@ -115,6 +116,16 @@ public abstract class AbstractOpenPgpStore extends Observable implements OpenPgp return keyStore.generateKeyRing(owner); } + @Override + public void importSecretKey(BareJid owner, PGPSecretKeyRing secretKeys) throws IOException, PGPException, MissingUserIdOnKeyException { + keyStore.importSecretKey(owner, secretKeys); + } + + @Override + public void importPublicKey(BareJid owner, PGPPublicKeyRing publicKeys) throws IOException, PGPException, MissingUserIdOnKeyException { + keyStore.importPublicKey(owner, publicKeys); + } + /* OpenPgpMetadataStore */ diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpTrustStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpTrustStore.java index ea953669d..29a2b0382 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpTrustStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/abstr/AbstractOpenPgpTrustStore.java @@ -1,3 +1,19 @@ +/** + * + * Copyright 2018 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.ox.store.abstr; import java.io.IOException; diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpKeyStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpKeyStore.java index 20c004afd..0528e5d6d 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpKeyStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpKeyStore.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,8 @@ import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException; + import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; @@ -42,4 +44,8 @@ public interface OpenPgpKeyStore { PGPSecretKeyRing getSecretKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException; PGPSecretKeyRing generateKeyRing(BareJid owner) throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException; + + void importSecretKey(BareJid owner, PGPSecretKeyRing secretKeys) throws IOException, PGPException, MissingUserIdOnKeyException; + + void importPublicKey(BareJid owner, PGPPublicKeyRing publicKeys) throws IOException, PGPException, MissingUserIdOnKeyException; } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpMetadataStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpMetadataStore.java index ed4715d23..7a2d291d9 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpMetadataStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpMetadataStore.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpStore.java index 87a6df8b6..25604c662 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpStore.java @@ -1,3 +1,19 @@ +/** + * + * Copyright 2018 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.ox.store.definition; import org.jivesoftware.smackx.ox.OpenPgpContact; diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpTrustStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpTrustStore.java index 294d6e061..13c812d7c 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpTrustStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/definition/OpenPgpTrustStore.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpKeyStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpKeyStore.java index cc2dce5d0..2df771111 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpKeyStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpKeyStore.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpMetadataStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpMetadataStore.java index 057bcf649..bcd6ae2a0 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpMetadataStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpMetadataStore.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpStore.java index 998c24456..abcd2bb19 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpStore.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpTrustStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpTrustStore.java index b51a95642..f66922a82 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpTrustStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpTrustStore.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 7ccd86a09..f456f37d3 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 @@ -468,7 +468,8 @@ public class PubSubDelegate { } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException | NoSuchFieldException e) { - throw new AssertionError("Using reflections to create a LeafNode and put it into PubSubManagers nodeMap failed.", e); + LOGGER.log(Level.SEVERE, "Using reflections to create a LeafNode and put it into PubSubManagers nodeMap failed.", e); + throw new AssertionError(e); } } } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java index 1e44523d2..01bd1163c 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java @@ -16,17 +16,22 @@ */ package org.jivesoftware.smackx.ox.util; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.SecureRandom; import java.util.Set; +import org.jivesoftware.smack.util.stringencoder.Base64; import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider; import org.jivesoftware.smackx.ox.element.SecretkeyElement; import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException; -import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException; -import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException; +import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.jxmpp.jid.BareJid; +import org.pgpainless.pgpainless.PGPainless; +import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint; public class SecretKeyBackupHelper { @@ -62,45 +67,41 @@ public class SecretKeyBackupHelper { public static SecretkeyElement createSecretkeyElement(OpenPgpProvider provider, BareJid owner, Set fingerprints, - String backupCode) throws SmackOpenPgpException, IOException { - /* + String backupCode) throws PGPException, IOException, MissingOpenPgpKeyPairException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + for (OpenPgpV4Fingerprint fingerprint : fingerprints) { - try { - byte[] bytes = provider.getStore().getSecretKeyRingBytes(owner, fingerprint); + + PGPSecretKeyRing key = provider.getStore().getSecretKeyRing(owner, fingerprint); + if (key == null) { + throw new MissingOpenPgpKeyPairException(owner, fingerprint); + } + + byte[] bytes = key.getEncoded(); buffer.write(bytes); - } catch (MissingOpenPgpKeyPairException | IOException e) { - throw new SmackOpenPgpException("Cannot backup secret key " + Long.toHexString(fingerprint.getKeyId()) + ".", e); - } } return createSecretkeyElement(provider, buffer.toByteArray(), backupCode); - */ - return null; } public static SecretkeyElement createSecretkeyElement(OpenPgpProvider provider, byte[] keys, String backupCode) - throws SmackOpenPgpException, IOException { - // byte[] encrypted = provider.symmetricallyEncryptWithPassword(keys, backupCode); - // return new SecretkeyElement(Base64.encode(encrypted)); - return null; + throws PGPException, IOException { + byte[] encrypted = PGPainless.encryptWithPassword(keys, backupCode.toCharArray(), SymmetricKeyAlgorithm.AES_256); + return new SecretkeyElement(Base64.encode(encrypted)); } - public static OpenPgpV4Fingerprint restoreSecretKeyBackup(OpenPgpProvider provider, SecretkeyElement backup, String backupCode) - throws InvalidBackupCodeException, IOException, MissingUserIdOnKeyException, SmackOpenPgpException { - /* + public static PGPSecretKeyRing restoreSecretKeyBackup(SecretkeyElement backup, String backupCode) + throws InvalidBackupCodeException, IOException, PGPException { byte[] encrypted = Base64.decode(backup.getB64Data()); byte[] decrypted; try { - decrypted = provider.symmetricallyDecryptWithPassword(encrypted, backupCode); - } catch (IOException | SmackOpenPgpException e) { + decrypted = PGPainless.decryptWithPassword(encrypted, backupCode.toCharArray()); + } catch (IOException | PGPException e) { throw new InvalidBackupCodeException("Could not decrypt secret key backup. Possibly wrong passphrase?", e); } - return provider.importSecretKey(decrypted); - */ - return null; + return PGPainless.readKeyRing().secretKeyRing(decrypted); } }