From 171cb07430ab6e3ad3836a8d5fa0f4840f3f02d2 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 3 Jul 2018 18:07:54 +0200 Subject: [PATCH] Temp --- .../ox/bouncycastle/DryOxEncryptionTest.java | 51 +++-- .../smackx/ox/OXInstantMessagingManager.java | 2 +- .../smackx/ox/OpenPgpManager.java | 125 +++--------- .../smackx/ox/OpenPgpProvider.java | 9 + .../smackx/ox/chat/OpenPgpContact.java | 193 +++++++++++++----- .../smackx/ox/chat/OpenPgpSelf.java | 18 ++ .../internal/FingerprintsChangedListener.java | 26 --- .../smackx/ox/util/PubSubDelegate.java | 4 +- 8 files changed, 223 insertions(+), 205 deletions(-) create mode 100644 smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpSelf.java delete mode 100644 smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/listener/internal/FingerprintsChangedListener.java diff --git a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/DryOxEncryptionTest.java b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/DryOxEncryptionTest.java index 0223290c8..4f90fc306 100644 --- a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/DryOxEncryptionTest.java +++ b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/DryOxEncryptionTest.java @@ -25,18 +25,19 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import org.jivesoftware.smack.DummyConnection; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.util.FileUtils; import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint; import org.jivesoftware.smackx.ox.TestKeys; import org.jivesoftware.smackx.ox.chat.OpenPgpContact; -import org.jivesoftware.smackx.ox.chat.OpenPgpFingerprints; import org.jivesoftware.smackx.ox.element.OpenPgpContentElement; import org.jivesoftware.smackx.ox.element.OpenPgpElement; import org.jivesoftware.smackx.ox.element.PubkeyElement; @@ -75,26 +76,29 @@ public class DryOxEncryptionTest extends OxTestSuite { @Test public void dryEncryptionTest() throws IOException, SmackOpenPgpException, MissingUserIdOnKeyException, MissingOpenPgpPublicKeyException, - MissingOpenPgpKeyPairException, XmlPullParserException { - BareJid juliet = TestKeys.JULIET_JID; - BareJid romemo = TestKeys.ROMEO_JID; + MissingOpenPgpKeyPairException, XmlPullParserException, SmackException.NotLoggedInException { + BareJid julietJid = TestKeys.JULIET_JID; + BareJid romeoJid = TestKeys.ROMEO_JID; + + XMPPConnection julietCon = new DummyConnection(); + XMPPConnection romeoCon = new DummyConnection(); FileBasedPainlessOpenPgpStore julietStore = new FileBasedPainlessOpenPgpStore(julietPath, new UnprotectedKeysProtector()); FileBasedPainlessOpenPgpStore romeoStore = new FileBasedPainlessOpenPgpStore(romeoPath, new UnprotectedKeysProtector()); - PainlessOpenPgpProvider julietProvider = new PainlessOpenPgpProvider(juliet, julietStore); - PainlessOpenPgpProvider romeoProvider = new PainlessOpenPgpProvider(romemo, romeoStore); + PainlessOpenPgpProvider julietProvider = new PainlessOpenPgpProvider(julietJid, julietStore); + PainlessOpenPgpProvider romeoProvider = new PainlessOpenPgpProvider(romeoJid, romeoStore); - OpenPgpV4Fingerprint julietFinger = julietProvider.importSecretKey(juliet, + OpenPgpV4Fingerprint julietFinger = julietProvider.importSecretKey(julietJid, BCUtil.getDecodedBytes(TestKeys.JULIET_PRIV.getBytes(UTF8))); - OpenPgpV4Fingerprint romeoFinger = romeoProvider.importSecretKey(romemo, + OpenPgpV4Fingerprint romeoFinger = romeoProvider.importSecretKey(romeoJid, BCUtil.getDecodedBytes(TestKeys.ROMEO_PRIV.getBytes(UTF8))); julietStore.setSigningKeyPairFingerprint(julietFinger); romeoStore.setSigningKeyPairFingerprint(romeoFinger); - byte[] julietPubBytes = julietStore.getPublicKeyRingBytes(juliet, julietFinger); - byte[] romeoPubBytes = romeoStore.getPublicKeyRingBytes(romemo, romeoFinger); + byte[] julietPubBytes = julietStore.getPublicKeyRingBytes(julietJid, julietFinger); + byte[] romeoPubBytes = romeoStore.getPublicKeyRingBytes(romeoJid, romeoFinger); assertNotNull(julietPubBytes); assertNotNull(romeoPubBytes); @@ -102,29 +106,20 @@ public class DryOxEncryptionTest extends OxTestSuite { assertTrue(romeoPubBytes.length != 0); PubkeyElement julietPub = new PubkeyElement(new PubkeyElement.PubkeyDataElement( - Base64.encode(julietStore.getPublicKeyRingBytes(juliet, julietFinger))), + Base64.encode(julietStore.getPublicKeyRingBytes(julietJid, julietFinger))), new Date()); PubkeyElement romeoPub = new PubkeyElement(new PubkeyElement.PubkeyDataElement( - Base64.encode(romeoStore.getPublicKeyRingBytes(romemo, romeoFinger))), + Base64.encode(romeoStore.getPublicKeyRingBytes(romeoJid, romeoFinger))), new Date()); - julietProvider.importPublicKey(romemo, Base64.decode(romeoPub.getDataElement().getB64Data())); - romeoProvider.importPublicKey(juliet, Base64.decode(julietPub.getDataElement().getB64Data())); + julietProvider.importPublicKey(romeoJid, Base64.decode(romeoPub.getDataElement().getB64Data())); + romeoProvider.importPublicKey(julietJid, Base64.decode(julietPub.getDataElement().getB64Data())); - julietStore.setAnnouncedKeysFingerprints(romemo, Collections.singletonMap(romeoFinger, new Date())); - romeoStore.setAnnouncedKeysFingerprints(juliet, Collections.singletonMap(julietFinger, new Date())); + julietStore.setAnnouncedKeysFingerprints(romeoJid, Collections.singletonMap(romeoFinger, new Date())); + romeoStore.setAnnouncedKeysFingerprints(julietJid, Collections.singletonMap(julietFinger, new Date())); - OpenPgpFingerprints julietFingerprints = new OpenPgpFingerprints(juliet, - Collections.singleton(julietFinger), - Collections.singleton(julietFinger), - new HashMap()); - OpenPgpFingerprints romeoFingerprints = new OpenPgpFingerprints(romemo, - Collections.singleton(romeoFinger), - Collections.singleton(romeoFinger), - new HashMap()); - - OpenPgpContact julietForRomeo = new OpenPgpContact(romeoProvider, juliet, romeoFingerprints, julietFingerprints); - OpenPgpContact romeoForJuliet = new OpenPgpContact(julietProvider, romemo, julietFingerprints, romeoFingerprints); + OpenPgpContact julietForRomeo = new OpenPgpContact(romeoProvider, julietJid, romeoCon); + OpenPgpContact romeoForJuliet = new OpenPgpContact(julietProvider, romeoJid, julietCon); String bodyText = "Finden wir eine Kompromisslösung – machen wir es so, wie ich es sage."; List payload = Collections.singletonList(new Message.Body("de", 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 3fe2f4eda..bd284f966 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 @@ -127,7 +127,7 @@ public final class OXInstantMessagingManager extends Manager implements Signcryp public void sendOxMessage(OpenPgpContact contact, CharSequence body) throws InterruptedException, MissingOpenPgpKeyPairException, IOException, - SmackException.NotConnectedException, SmackOpenPgpException { + SmackException.NotConnectedException, SmackOpenPgpException, SmackException.NotLoggedInException { Message message = new Message(contact.getJid()); List payload = new ArrayList<>(); payload.add(new Message.Body(null, body.toString())); 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 4daf9f3c4..e2d719766 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,7 +18,6 @@ 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.fetchPubkey; import static org.jivesoftware.smackx.ox.util.PubSubDelegate.publishPublicKey; import java.io.IOException; @@ -26,7 +25,6 @@ import java.io.InputStream; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; -import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -52,7 +50,7 @@ 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.chat.OpenPgpContact; -import org.jivesoftware.smackx.ox.chat.OpenPgpFingerprints; +import org.jivesoftware.smackx.ox.chat.OpenPgpSelf; import org.jivesoftware.smackx.ox.element.CryptElement; import org.jivesoftware.smackx.ox.element.OpenPgpContentElement; import org.jivesoftware.smackx.ox.element.OpenPgpElement; @@ -106,6 +104,8 @@ public final class OpenPgpManager extends Manager { private final Set signElementReceivedListeners = new HashSet<>(); private final Set cryptElementReceivedListeners = new HashSet<>(); + private OpenPgpSelf self; + /** * Private constructor to avoid instantiation without putting the object into {@code INSTANCES}. * @@ -141,6 +141,16 @@ public final class OpenPgpManager extends Manager { this.provider = provider; } + public OpenPgpSelf getOpenPgpSelf() throws SmackException.NotLoggedInException { + throwIfNotAuthenticated(); + + if (self == null) { + self = new OpenPgpSelf(provider, connection().getUser().asBareJid(), connection()); + } + + return self; + } + /** * Generate a fresh OpenPGP key pair, given we don't have one already. * Publish the public key to the Public Key Node and update the Public Key Metadata Node with our keys fingerprint. @@ -220,21 +230,13 @@ public final class OpenPgpManager extends Manager { * * @param jid {@link BareJid} of the contact. * @return {@link OpenPgpContact}. - * @throws SmackOpenPgpException if something happens while gathering fingerprints. - * @throws InterruptedException - * @throws XMPPException.XMPPErrorException */ - public OpenPgpContact getOpenPgpContact(EntityBareJid jid) - throws SmackOpenPgpException, InterruptedException, XMPPException.XMPPErrorException, - SmackException.NotLoggedInException { - throwIfNotAuthenticated(); + public OpenPgpContact getOpenPgpContact(EntityBareJid jid) { OpenPgpContact openPgpContact = openPgpCapableContacts.get(jid); if (openPgpContact == null) { - OpenPgpFingerprints theirKeys = determineContactsKeys(jid); - OpenPgpFingerprints ourKeys = determineContactsKeys(connection().getUser().asBareJid()); - openPgpContact = new OpenPgpContact(provider, jid, ourKeys, theirKeys); + openPgpContact = new OpenPgpContact(provider, jid, connection()); openPgpCapableContacts.put(jid, openPgpContact); } @@ -343,45 +345,6 @@ public final class OpenPgpManager extends Manager { return fingerprint; } - /** - * Determine which keys belong to a user and fetch any missing keys. - * - * @param jid {@link BareJid} of the user in question. - * @return {@link OpenPgpFingerprints} object containing the announced, available and unfetchable keys of the user. - * @throws SmackOpenPgpException - * @throws InterruptedException - * @throws XMPPException.XMPPErrorException - */ - private OpenPgpFingerprints determineContactsKeys(BareJid jid) - throws SmackOpenPgpException, InterruptedException, XMPPException.XMPPErrorException { - Set announced = provider.getStore().getAnnouncedKeysFingerprints(jid).keySet(); - Set available = provider.getStore().getAvailableKeysFingerprints(jid).keySet(); - Map unfetched = new HashMap<>(); - for (OpenPgpV4Fingerprint f : announced) { - if (!available.contains(f)) { - try { - PubkeyElement pubkeyElement = PubSubDelegate.fetchPubkey(connection(), jid, f); - if (pubkeyElement == null) { - continue; - } - - processPublicKey(pubkeyElement, jid); - available.add(f); - - } 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) { - LOGGER.log(Level.WARNING, "Key does not contain user-id of " + jid + ". Ignoring the key.", e); - unfetched.put(f, e); - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Could not import key " + f.toString() + " of user " + jid.toString(), e); - } - } - } - return new OpenPgpFingerprints(jid, announced, available, unfetched); - } - /** * {@link PEPListener} that listens for changes to the OX public keys metadata node. * @@ -407,52 +370,13 @@ public final class OpenPgpManager extends Manager { } }; - public void requestMetadataUpdate(BareJid contact) - throws InterruptedException, SmackException, - XMPPException.XMPPErrorException { - PublicKeysListElement metadata = PubSubDelegate.fetchPubkeysList(connection(), contact); - processPublicKeysListElement(contact, metadata); - } - private void processPublicKeysListElement(BareJid contact, PublicKeysListElement listElement) { - Map announcedKeys = new HashMap<>(); - for (OpenPgpV4Fingerprint f : listElement.getMetadata().keySet()) { - PublicKeysListElement.PubkeyMetadataElement meta = listElement.getMetadata().get(f); - announcedKeys.put(meta.getV4Fingerprint(), meta.getDate()); - } - provider.getStore().setAnnouncedKeysFingerprints(contact, announcedKeys); - - Set availableKeys = Collections.emptySet(); + OpenPgpContact openPgpContact = getOpenPgpContact(contact.asEntityBareJidIfPossible()); try { - availableKeys = new HashSet<>(provider.getStore().getAvailableKeysFingerprints(contact).keySet()); + openPgpContact.updateKeys(listElement); } catch (SmackOpenPgpException e) { - LOGGER.log(Level.WARNING, "Could not determine available keys", e); - } - - Set missingKeys = listElement.getMetadata().keySet(); - Map unfetchable = new HashMap<>(); - try { - missingKeys.removeAll(availableKeys); - for (OpenPgpV4Fingerprint missing : missingKeys) { - try { - PubkeyElement pubkeyElement = fetchPubkey(connection(), contact, missing); - if (pubkeyElement != null) { - processPublicKey(pubkeyElement, contact); - availableKeys.add(missing); - } - } catch (Exception e) { - LOGGER.log(Level.WARNING, "Error fetching missing OpenPGP key " + missing.toString(), e); - unfetchable.put(missing, e); - } - } - } catch (Exception e) { - LOGGER.log(Level.WARNING, "Error processing OpenPGP metadata update from " + contact + ".", e); - } - - OpenPgpFingerprints fingerprints = new OpenPgpFingerprints(contact, announcedKeys.keySet(), availableKeys, unfetchable); - for (OpenPgpContact openPgpContact : openPgpCapableContacts.values()) { - openPgpContact.onFingerprintsChanged(contact, fingerprints); + LOGGER.log(Level.WARNING, "Could not read key ring of contact " + contact, e); } } @@ -466,14 +390,7 @@ public final class OpenPgpManager extends Manager { return; } - OpenPgpContact contact; - try { - contact = getOpenPgpContact(from); - } catch (SmackOpenPgpException | InterruptedException | XMPPException.XMPPErrorException | - SmackException.NotLoggedInException e) { - LOGGER.log(Level.WARNING, "Could not begin encrypted chat with " + from, e); - return; - } + OpenPgpContact contact = getOpenPgpContact(from); OpenPgpContentElement contentElement = null; try { @@ -506,6 +423,10 @@ public final class OpenPgpManager extends Manager { } return; } + + else { + throw new AssertionError("Invalid element received: " + contentElement.getClass().getName()); + } } }; diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java index 52fb2fcc2..58baeabdc 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java @@ -142,6 +142,15 @@ public interface OpenPgpProvider { throws SmackOpenPgpException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException, IOException; + /** + * Import a public key. The bytes are expected to be decoded from base64. + * @param owner + * @param bytes + * @return + * @throws MissingUserIdOnKeyException + * @throws IOException + * @throws SmackOpenPgpException + */ OpenPgpV4Fingerprint importPublicKey(BareJid owner, byte[] bytes) throws MissingUserIdOnKeyException, IOException, SmackOpenPgpException; diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpContact.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpContact.java index 70490a7c6..56e14ea1f 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpContact.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpContact.java @@ -18,10 +18,17 @@ package org.jivesoftware.smackx.ox.chat; import java.io.IOException; import java.util.Collections; +import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.chat2.ChatManager; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.Message; @@ -29,51 +36,168 @@ import org.jivesoftware.smack.util.MultiMap; import org.jivesoftware.smack.util.stringencoder.Base64; import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement; import org.jivesoftware.smackx.hints.element.StoreHint; +import org.jivesoftware.smackx.ox.OpenPgpManager; import org.jivesoftware.smackx.ox.OpenPgpProvider; import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint; 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.SigncryptElement; 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.SmackOpenPgpException; -import org.jivesoftware.smackx.ox.listener.internal.FingerprintsChangedListener; import org.jivesoftware.smackx.ox.util.DecryptedBytesAndMetadata; +import org.jivesoftware.smackx.ox.util.PubSubDelegate; import org.jxmpp.jid.BareJid; import org.jxmpp.jid.Jid; import org.xmlpull.v1.XmlPullParserException; -public class OpenPgpContact implements FingerprintsChangedListener { +public class OpenPgpContact { + + private static final Logger LOGGER = Logger.getLogger(OpenPgpContact.class.getName()); private final BareJid jid; - private OpenPgpFingerprints contactsFingerprints; - private OpenPgpFingerprints ourFingerprints; - private final OpenPgpProvider cryptoProvider; - private final OpenPgpV4Fingerprint singingKey; + protected final OpenPgpProvider cryptoProvider; + private final XMPPConnection connection; + + private Map announcedKeys = null; + private Map availableKeys = null; + private final Map unfetchableKeys = new HashMap<>(); public OpenPgpContact(OpenPgpProvider cryptoProvider, BareJid jid, - OpenPgpFingerprints ourFingerprints, - OpenPgpFingerprints contactsFingerprints) { - this.cryptoProvider = cryptoProvider; + XMPPConnection connection) { this.jid = jid; - this.singingKey = cryptoProvider.getStore().getSigningKeyPairFingerprint(); - this.ourFingerprints = ourFingerprints; - this.contactsFingerprints = contactsFingerprints; + this.cryptoProvider = cryptoProvider; + this.connection = connection; } public BareJid getJid() { return jid; } - public OpenPgpFingerprints getFingerprints() { - return contactsFingerprints; + public Map getAnnouncedKeys() { + if (announcedKeys == null) { + announcedKeys = cryptoProvider.getStore().getAnnouncedKeysFingerprints(getJid()); + } + return announcedKeys; + } + + public Map getAvailableKeys() throws SmackOpenPgpException { + if (availableKeys == null) { + availableKeys = cryptoProvider.getStore().getAvailableKeysFingerprints(getJid()); + } + return availableKeys; + } + + public Map getUnfetchableKeys() { + return unfetchableKeys; + } + + public Set getActiveKeys() throws SmackOpenPgpException { + Set fingerprints = getAvailableKeys().keySet(); + fingerprints.retainAll(getAnnouncedKeys().keySet()); + return fingerprints; + } + + public void updateKeys() + throws InterruptedException, XMPPException.XMPPErrorException, SmackException, SmackOpenPgpException { + updateKeys(PubSubDelegate.fetchPubkeysList(connection, getJid())); + } + + public void updateKeys(PublicKeysListElement metadata) + throws SmackOpenPgpException { + storePublishedDevices(metadata); + this.availableKeys = getAvailableKeys(); + + for (OpenPgpV4Fingerprint fingerprint : announcedKeys.keySet()) { + Date announcedDate = announcedKeys.get(fingerprint); + Date availableDate = availableKeys.get(fingerprint); + + if (availableDate == null || availableDate.before(announcedDate)) { + try { + updateKey(fingerprint); + unfetchableKeys.remove(fingerprint); + } catch (IOException | XMPPException.XMPPErrorException | SmackException | InterruptedException | + SmackOpenPgpException | MissingUserIdOnKeyException | NullPointerException e) { + LOGGER.log(Level.WARNING, "Could not update key " + fingerprint + " of " +getJid()); + unfetchableKeys.put(fingerprint, e); + } + } + } + } + + public void updateKey(OpenPgpV4Fingerprint fingerprint) + throws InterruptedException, XMPPException.XMPPErrorException, SmackException, IOException, + MissingUserIdOnKeyException, SmackOpenPgpException { + PubkeyElement pubkeyElement = PubSubDelegate.fetchPubkey(connection, getJid(), fingerprint); + if (pubkeyElement == null) { + throw new NullPointerException("Fetched pubkeyElement for key " + fingerprint + " of " + getJid() + " is null."); + } + + byte[] base64 = pubkeyElement.getDataElement().getB64Data(); + OpenPgpV4Fingerprint imported = importPublicKey(Base64.decode(base64)); + + if (!fingerprint.equals(imported)) { + // Not sure, if this can/should happen. Lets be safe and throw, even if its too late at this point. + throw new AssertionError("Fingerprint of imported key differs from expected fingerprint. " + + "Expected: " + fingerprint + " Imported: " + imported); + } + } + + private OpenPgpV4Fingerprint importPublicKey(byte[] data) + throws SmackOpenPgpException, MissingUserIdOnKeyException, IOException { + OpenPgpV4Fingerprint fingerprint = cryptoProvider.importPublicKey(getJid(), data); + availableKeys.put(fingerprint, new Date()); + return fingerprint; + } + + public Map storePublishedDevices(PublicKeysListElement element) { + Map announcedKeys = new HashMap<>(); + + for (OpenPgpV4Fingerprint f : element.getMetadata().keySet()) { + PublicKeysListElement.PubkeyMetadataElement meta = element.getMetadata().get(f); + announcedKeys.put(meta.getV4Fingerprint(), meta.getDate()); + } + + if (!announcedKeys.equals(this.announcedKeys)) { + cryptoProvider.getStore().setAnnouncedKeysFingerprints(getJid(), announcedKeys); + this.announcedKeys = announcedKeys; + } + return announcedKeys; + } + + private MultiMap getEncryptionKeys() + throws SmackOpenPgpException, SmackException.NotLoggedInException { + OpenPgpSelf self = getSelf(); + + Set contactsKeys = getActiveKeys(); + Set ourKeys = self.getActiveKeys(); + + MultiMap recipientsKeys = new MultiMap<>(); + for (OpenPgpV4Fingerprint fingerprint : contactsKeys) { + recipientsKeys.put(getJid(), fingerprint); + } + + for (OpenPgpV4Fingerprint fingerprint : ourKeys) { + recipientsKeys.put(self.getJid(), fingerprint); + } + + return recipientsKeys; + } + + private OpenPgpSelf getSelf() throws SmackException.NotLoggedInException { + return OpenPgpManager.getInstanceFor(connection).getOpenPgpSelf(); } public OpenPgpElement encryptAndSign(List payload) - throws IOException, SmackOpenPgpException, MissingOpenPgpKeyPairException { - MultiMap fingerprints = oursAndRecipientFingerprints(); + throws IOException, SmackOpenPgpException, MissingOpenPgpKeyPairException, + SmackException.NotLoggedInException { + + OpenPgpSelf self = OpenPgpManager.getInstanceFor(connection).getOpenPgpSelf(); SigncryptElement preparedPayload = new SigncryptElement( Collections.singleton(getJid()), @@ -85,8 +209,8 @@ public class OpenPgpContact implements FingerprintsChangedListener { try { encryptedBytes = cryptoProvider.signAndEncrypt( preparedPayload, - singingKey, - fingerprints); + self.getSigningKey(), + getEncryptionKeys()); } catch (MissingOpenPgpPublicKeyException e) { throw new AssertionError("Missing OpenPGP public key, even though this should not happen here.", e); } @@ -95,7 +219,8 @@ public class OpenPgpContact implements FingerprintsChangedListener { } public void addSignedEncryptedPayloadTo(Message message, List payload) - throws IOException, SmackOpenPgpException, MissingOpenPgpKeyPairException { + throws IOException, SmackOpenPgpException, MissingOpenPgpKeyPairException, + SmackException.NotLoggedInException { // Add encrypted payload to message OpenPgpElement encryptedPayload = encryptAndSign(payload); @@ -114,8 +239,8 @@ public class OpenPgpContact implements FingerprintsChangedListener { public void send(XMPPConnection connection, Message message, List payload) throws MissingOpenPgpKeyPairException, SmackException.NotConnectedException, InterruptedException, - SmackOpenPgpException, IOException { - MultiMap fingerprints = oursAndRecipientFingerprints(); + SmackOpenPgpException, IOException, SmackException.NotLoggedInException { + MultiMap fingerprints = getEncryptionKeys(); SigncryptElement preparedPayload = new SigncryptElement( Collections.singleton(getJid()), @@ -128,7 +253,7 @@ public class OpenPgpContact implements FingerprintsChangedListener { try { encryptedMessage = cryptoProvider.signAndEncrypt( preparedPayload, - singingKey, + getSelf().getSigningKey(), fingerprints); } catch (MissingOpenPgpPublicKeyException e) { throw new AssertionError("Missing OpenPGP public key, even though this should not happen here.", e); @@ -154,28 +279,4 @@ public class OpenPgpContact implements FingerprintsChangedListener { return openPgpMessage.getOpenPgpContentElement(); } - - private MultiMap oursAndRecipientFingerprints() { - MultiMap fingerprints = new MultiMap<>(); - for (OpenPgpV4Fingerprint f : contactsFingerprints.getActiveKeys()) { - fingerprints.put(contactsFingerprints.getJid(), f); - } - - if (!contactsFingerprints.getJid().equals(ourFingerprints.getJid())) { - for (OpenPgpV4Fingerprint f : ourFingerprints.getActiveKeys()) { - fingerprints.put(ourFingerprints.getJid(), f); - } - } - - return fingerprints; - } - - @Override - public void onFingerprintsChanged(BareJid contact, OpenPgpFingerprints newFingerprints) { - if (ourFingerprints.getJid().equals(contact)) { - this.ourFingerprints = newFingerprints; - } else if (contactsFingerprints.getJid().equals(contact)) { - this.contactsFingerprints = newFingerprints; - } - } } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpSelf.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpSelf.java new file mode 100644 index 000000000..139d40574 --- /dev/null +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpSelf.java @@ -0,0 +1,18 @@ +package org.jivesoftware.smackx.ox.chat; + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smackx.ox.OpenPgpProvider; +import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint; + +import org.jxmpp.jid.BareJid; + +public class OpenPgpSelf extends OpenPgpContact { + + public OpenPgpSelf(OpenPgpProvider cryptoProvider, BareJid jid, XMPPConnection connection) { + super(cryptoProvider, jid, connection); + } + + public OpenPgpV4Fingerprint getSigningKey() { + return cryptoProvider.getStore().getSigningKeyPairFingerprint(); + } +} diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/listener/internal/FingerprintsChangedListener.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/listener/internal/FingerprintsChangedListener.java deleted file mode 100644 index 2974f3878..000000000 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/listener/internal/FingerprintsChangedListener.java +++ /dev/null @@ -1,26 +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.listener.internal; - -import org.jivesoftware.smackx.ox.chat.OpenPgpFingerprints; - -import org.jxmpp.jid.BareJid; - -public interface FingerprintsChangedListener { - - void onFingerprintsChanged(BareJid contact, OpenPgpFingerprints newFingerprints); -} 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 1b91405d7..27adc933c 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 @@ -212,7 +212,7 @@ public class PubSubDelegate { try { pm.deleteNode(PEP_NODE_PUBLIC_KEYS); } catch (XMPPException.XMPPErrorException e) { - if (e.getXMPPError().getCondition() == StanzaError.Condition.item_not_found) { + if (e.getStanzaError().getCondition() == StanzaError.Condition.item_not_found) { LOGGER.log(Level.FINE, "Node does not exist. No need to delete it."); } else { throw e; @@ -237,7 +237,7 @@ public class PubSubDelegate { try { pm.deleteNode(PEP_NODE_PUBLIC_KEY(fingerprint)); } catch (XMPPException.XMPPErrorException e) { - if (e.getXMPPError().getCondition() == StanzaError.Condition.item_not_found) { + if (e.getStanzaError().getCondition() == StanzaError.Condition.item_not_found) { LOGGER.log(Level.FINE, "Node does not exist. No need to delete it."); } else { throw e;