1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-06-15 08:04:49 +02:00
Smack/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java
Paul Schaub 8850a2950c
Remove unnecessary lines from OX backup restore function
The manual construction of the public key ring as
done in the removed lines is already performed in the BCUtil
method called a line above.
2020-07-04 20:02:33 +02:00

703 lines
32 KiB
Java

/**
*
* Copyright 2017-2020 Florian Schmaus, 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 static org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil.PEP_NODE_PUBLIC_KEYS;
import static org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil.PEP_NODE_PUBLIC_KEYS_NOTIFY;
import static org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil.publishPublicKey;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.chat2.ChatManager;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
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.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.MissingOpenPgpKeyException;
import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException;
import org.jivesoftware.smackx.ox.exception.NoBackupFoundException;
import org.jivesoftware.smackx.ox.listener.CryptElementReceivedListener;
import org.jivesoftware.smackx.ox.listener.SignElementReceivedListener;
import org.jivesoftware.smackx.ox.listener.SigncryptElementReceivedListener;
import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore;
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil;
import org.jivesoftware.smackx.ox.util.SecretKeyBackupHelper;
import org.jivesoftware.smackx.pep.PepEventListener;
import org.jivesoftware.smackx.pep.PepListener;
import org.jivesoftware.smackx.pep.PepManager;
import org.jivesoftware.smackx.pubsub.LeafNode;
import org.jivesoftware.smackx.pubsub.PubSubException;
import org.jivesoftware.smackx.pubsub.PubSubFeature;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.collection.PGPKeyRing;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.util.BCUtil;
/**
* Entry point for Smacks API for OpenPGP for XMPP.
*
* <h2>Setup</h2>
*
* In order to use OpenPGP for XMPP in Smack, just follow the following procedure.<br>
* <br>
* First, acquire an instance of the {@link OpenPgpManager} for your {@link XMPPConnection} using
* {@link #getInstanceFor(XMPPConnection)}.
*
* <pre>
* {@code
* OpenPgpManager openPgpManager = OpenPgpManager.getInstanceFor(connection);
* }
* </pre>
*
* You also need an {@link OpenPgpProvider}, as well as an {@link OpenPgpStore}.
* The provider must be registered using {@link #setOpenPgpProvider(OpenPgpProvider)}.
*
* <pre>
* {@code
* OpenPgpStore store = new FileBasedOpenPgpStore(storePath);
* OpenPgpProvider provider = new PainlessOpenPgpProvider(connection, store);
* openPgpManager.setOpenPgpProvider(provider);
* }
* </pre>
*
* It is also advised to register a custom {@link SecretKeyRingProtector} using
* {@link OpenPgpStore#setKeyRingProtector(SecretKeyRingProtector)} in order to be able to handle password protected
* secret keys.<br>
* <br>
* Speaking of keys, you can now check, if you have any keys available in your {@link OpenPgpStore} by doing
* {@link #hasSecretKeysAvailable()}.<br>
* <br>
* If you do, you can now announce support for OX and publish those keys using {@link #announceSupportAndPublish()}.<br>
* <br>
* Otherwise, you can either generate fresh keys using {@link #generateAndImportKeyPair(BareJid)},
* or try to restore a secret key backup from your private PubSub node by doing
* {@link #restoreSecretKeyServerBackup(AskForBackupCodeCallback)}.<br>
* <br>
* In any case you should still do an {@link #announceSupportAndPublish()} afterwards.
* <br>
* <br>
* Contacts are represented by {@link OpenPgpContact}s in the context of OpenPGP for XMPP. You can get those by using
* {@link #getOpenPgpContact(EntityBareJid)}. The main function of {@link OpenPgpContact}s is to bundle information
* about the OpenPGP capabilities of a contact in one spot. The pendant to the {@link OpenPgpContact} is the
* {@link OpenPgpSelf}, which encapsulates your own OpenPGP identity. Both classes can be used to acquire information
* about the OpenPGP keys of a user.
*
* <h2>Elements</h2>
*
* OpenPGP for XMPP defines multiple different element classes which contain the users messages.
* The outermost element is the {@link OpenPgpElement}, which contains an OpenPGP encrypted content element.
*
* The content can be either a {@link SignElement}, {@link CryptElement} or {@link SigncryptElement}, depending on the use-case.
* Those content elements contain the actual payload. If an {@link OpenPgpElement} is decrypted, it will be returned in
* form of an {@link OpenPgpMessage}, which represents the decrypted message + metadata.
*
* @see <a href="https://xmpp.org/extensions/xep-0373.html">
* XEP-0373: OpenPGP for XMPP</a>
*/
public final class OpenPgpManager extends Manager {
private static final Logger LOGGER = Logger.getLogger(OpenPgpManager.class.getName());
/**
* Map of instances.
*/
private static final Map<XMPPConnection, OpenPgpManager> INSTANCES = new WeakHashMap<>();
/**
* {@link OpenPgpProvider} responsible for processing keys, encrypting and decrypting messages and so on.
*/
private OpenPgpProvider provider;
private final PepManager pepManager;
private final Set<SigncryptElementReceivedListener> signcryptElementReceivedListeners = new HashSet<>();
private final Set<SignElementReceivedListener> signElementReceivedListeners = new HashSet<>();
private final Set<CryptElementReceivedListener> cryptElementReceivedListeners = new HashSet<>();
@SuppressWarnings("UnnecessaryLambda")
private final PepEventListener<PublicKeysListElement> pepPublicKeyListElementListener = (from, listElement, id, message) -> processPublicKeysListElement(from, listElement);;
/**
* Private constructor to avoid instantiation without putting the object into {@code INSTANCES}.
*
* @param connection xmpp connection.
*/
private OpenPgpManager(XMPPConnection connection) {
super(connection);
ChatManager.getInstanceFor(connection).addIncomingListener(this::incomingChatMessageListener);
pepManager = PepManager.getInstanceFor(connection);
}
/**
* Get the instance of the {@link OpenPgpManager} which belongs to the {@code connection}.
*
* @param connection xmpp connection.
* @return instance of the manager.
*/
public static synchronized OpenPgpManager getInstanceFor(XMPPConnection connection) {
OpenPgpManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new OpenPgpManager(connection);
INSTANCES.put(connection, manager);
}
return manager;
}
/**
* Return our own {@link BareJid}.
*
* @return our bareJid
*
* @throws SmackException.NotLoggedInException in case our connection is not logged in, which means our BareJid is unknown.
*/
public BareJid getJidOrThrow() throws SmackException.NotLoggedInException {
throwIfNotAuthenticated();
return connection().getUser().asEntityBareJidOrThrow();
}
/**
* Set the {@link OpenPgpProvider} which will be used to process incoming OpenPGP elements,
* as well as to execute cryptographic operations.
*
* @param provider OpenPgpProvider.
*/
public void setOpenPgpProvider(OpenPgpProvider provider) {
this.provider = provider;
}
public OpenPgpProvider getOpenPgpProvider() {
return provider;
}
/**
* Get our OpenPGP self.
*
* @return self TODO javadoc me please
* @throws SmackException.NotLoggedInException if we are not logged in
*/
public OpenPgpSelf getOpenPgpSelf() throws SmackException.NotLoggedInException {
throwIfNoProviderSet();
return new OpenPgpSelf(getJidOrThrow(), provider.getStore());
}
/**
* 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.
* Lastly register a {@link PepListener} which listens for updates to Public Key Metadata Nodes.
*
* @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 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.
* @throws SmackException.NotConnectedException if we are not connected.
* @throws SmackException.NoResponseException if the server doesn't respond.
* @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, InterruptedException,
PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, SmackException.NoResponseException, IOException,
InvalidAlgorithmParameterException, SmackException.NotLoggedInException, PGPException {
throwIfNoProviderSet();
throwIfNotAuthenticated();
OpenPgpV4Fingerprint primaryFingerprint = getOurFingerprint();
if (primaryFingerprint == null) {
primaryFingerprint = generateAndImportKeyPair(getJidOrThrow());
}
// Create <pubkey/> element
PubkeyElement pubkeyElement;
try {
pubkeyElement = createPubkeyElement(getJidOrThrow(), primaryFingerprint, new Date());
} catch (MissingOpenPgpKeyException e) {
throw new AssertionError("Cannot publish our public key, since it is missing (MUST NOT happen!)");
}
// publish it
publishPublicKey(pepManager, pubkeyElement, primaryFingerprint);
// Subscribe to public key changes
pepManager.addPepEventListener(PEP_NODE_PUBLIC_KEYS, PublicKeysListElement.class, pepPublicKeyListElementListener);
ServiceDiscoveryManager.getInstanceFor(connection())
.addFeature(PEP_NODE_PUBLIC_KEYS_NOTIFY);
}
/**
* Generate a fresh OpenPGP key pair and import it.
*
* @param ourJid our {@link BareJid}.
* @return {@link OpenPgpV4Fingerprint} of the generated key.
* @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 PGPException PGP is brittle.
* @throws IOException IO is dangerous.
*/
public OpenPgpV4Fingerprint generateAndImportKeyPair(BareJid ourJid)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException,
PGPException, IOException {
throwIfNoProviderSet();
OpenPgpStore store = provider.getStore();
PGPKeyRing keys = store.generateKeyRing(ourJid);
try {
store.importSecretKey(ourJid, keys.getSecretKeys());
store.importPublicKey(ourJid, keys.getPublicKeys());
} catch (MissingUserIdOnKeyException e) {
// This should never throw, since we set our jid literally one line above this comment.
throw new AssertionError(e);
}
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(keys.getSecretKeys());
store.setTrust(ourJid, fingerprint, OpenPgpTrustStore.Trust.trusted);
return fingerprint;
}
/**
* Return the upper-case hex encoded OpenPGP v4 fingerprint of our key pair.
*
* @return fingerprint.
* @throws SmackException.NotLoggedInException in case we are not logged in.
* @throws IOException IO is dangerous.
* @throws PGPException PGP is brittle.
*/
public OpenPgpV4Fingerprint getOurFingerprint()
throws SmackException.NotLoggedInException, IOException, PGPException {
return getOpenPgpSelf().getSigningKeyFingerprint();
}
/**
* Return an OpenPGP capable contact.
* This object can be used as an entry point to OpenPGP related API.
*
* @param jid {@link BareJid} of the contact.
* @return {@link OpenPgpContact}.
*/
public OpenPgpContact getOpenPgpContact(EntityBareJid jid) {
throwIfNoProviderSet();
return provider.getStore().getOpenPgpContact(jid);
}
/**
* Return true, if we have a secret key available, otherwise false.
*
* @return true if secret key available
*
* @throws SmackException.NotLoggedInException If we are not logged in (we need to know our jid in order to look up
* our keys in the key store.
* @throws PGPException in case the keys in the store are damaged somehow.
* @throws IOException IO is dangerous.
*/
public boolean hasSecretKeysAvailable() throws SmackException.NotLoggedInException, PGPException, IOException {
throwIfNoProviderSet();
return getOpenPgpSelf().hasSecretKeyAvailable();
}
/**
* Determine, if we can sync secret keys using private PEP nodes as described in the XEP.
* Requirements on the server side are support for PEP and support for the whitelist access model of PubSub.
*
* @see <a href="https://xmpp.org/extensions/xep-0373.html#synchro-pep">XEP-0373 §5</a>
*
* @param connection XMPP connection
* @return true, if the server supports secret key backups, otherwise false.
* @throws XMPPException.XMPPErrorException in case of an XMPP protocol error.
* @throws SmackException.NotConnectedException if we are not connected.
* @throws InterruptedException if the thread is interrupted.
* @throws SmackException.NoResponseException if the server doesn't respond.
*/
public static boolean serverSupportsSecretKeyBackups(XMPPConnection connection)
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException {
return ServiceDiscoveryManager.getInstanceFor(connection)
.serverSupportsFeature(PubSubFeature.access_whitelist.toString());
}
/**
* Remove the metadata listener. This method is mainly used in tests.
*/
public void stopMetadataListener() {
pepManager.removePepEventListener(pepPublicKeyListElementListener);
}
/**
* Upload the encrypted secret key to a private PEP node.
*
* @see <a href="https://xmpp.org/extensions/xep-0373.html#synchro-pep">XEP-0373 §5</a>
*
* @param displayCodeCallback callback, which will receive the backup password used to encrypt the secret key.
* @param selectKeyCallback callback, which will receive the users choice of which keys will be backed up.
* @throws InterruptedException if the thread is interrupted.
* @throws PubSubException.NotALeafNodeException if the private node is not a {@link LeafNode}.
* @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 SmackException.NotLoggedInException if we are not logged in.
* @throws IOException IO is dangerous.
* @throws SmackException.FeatureNotSupportedException if the server doesn't support the PubSub whitelist access model.
* @throws PGPException PGP is brittle
* @throws MissingOpenPgpKeyException in case we have no OpenPGP key pair to back up.
*/
public void backupSecretKeyToServer(DisplayBackupCodeCallback displayCodeCallback,
SecretKeyBackupSelectionCallback selectKeyCallback)
throws InterruptedException, PubSubException.NotALeafNodeException,
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException,
SmackException.NotLoggedInException, IOException,
SmackException.FeatureNotSupportedException, PGPException, MissingOpenPgpKeyException {
throwIfNoProviderSet();
throwIfNotAuthenticated();
BareJid ownJid = connection().getUser().asBareJid();
String backupCode = SecretKeyBackupHelper.generateBackupPassword();
PGPSecretKeyRingCollection secretKeyRings = provider.getStore().getSecretKeysOf(ownJid);
Set<OpenPgpV4Fingerprint> availableKeyPairs = new HashSet<>();
for (PGPSecretKeyRing ring : secretKeyRings) {
availableKeyPairs.add(new OpenPgpV4Fingerprint(ring));
}
Set<OpenPgpV4Fingerprint> selectedKeyPairs = selectKeyCallback.selectKeysToBackup(availableKeyPairs);
SecretkeyElement secretKey = SecretKeyBackupHelper.createSecretkeyElement(provider, ownJid, selectedKeyPairs, backupCode);
OpenPgpPubSubUtil.depositSecretKey(connection(), secretKey);
displayCodeCallback.displayBackupCode(backupCode);
}
/**
* Delete the private {@link LeafNode} containing our secret key backup.
*
* @throws XMPPException.XMPPErrorException in case of an XMPP protocol error.
* @throws SmackException.NotConnectedException if we are not connected.
* @throws InterruptedException if the thread gets interrupted.
* @throws SmackException.NoResponseException if the server doesn't respond.
* @throws SmackException.NotLoggedInException if we are not logged in.
*/
public void deleteSecretKeyServerBackup()
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException, SmackException.NotLoggedInException {
throwIfNotAuthenticated();
OpenPgpPubSubUtil.deleteSecretKeyNode(pepManager);
}
/**
* Fetch a secret key backup from the server and try to restore a selected secret key from it.
*
* @param codeCallback callback for prompting the user to provide the secret backup code.
* @return fingerprint of the restored secret key
*
* @throws InterruptedException if the thread gets interrupted.
* @throws PubSubException.NotALeafNodeException if the private node is not a {@link LeafNode}.
* @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 InvalidBackupCodeException if the user-provided backup code is invalid.
* @throws SmackException.NotLoggedInException if we are not logged in
* @throws IOException IO is dangerous
* @throws MissingUserIdOnKeyException if the key that is to be imported is missing a user-id with our jid
* @throws NoBackupFoundException if no secret key backup has been found
* @throws PGPException in case the restored secret key is damaged.
*/
public OpenPgpV4Fingerprint restoreSecretKeyServerBackup(AskForBackupCodeCallback codeCallback)
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, SmackException.NoResponseException,
InvalidBackupCodeException, SmackException.NotLoggedInException, IOException, MissingUserIdOnKeyException,
NoBackupFoundException, PGPException {
throwIfNoProviderSet();
throwIfNotAuthenticated();
SecretkeyElement backup = OpenPgpPubSubUtil.fetchSecretKey(pepManager);
if (backup == null) {
throw new NoBackupFoundException();
}
String backupCode = codeCallback.askForBackupCode();
PGPSecretKeyRing secretKeys = SecretKeyBackupHelper.restoreSecretKeyBackup(backup, backupCode);
provider.getStore().importSecretKey(getJidOrThrow(), secretKeys);
provider.getStore().importPublicKey(getJidOrThrow(), BCUtil.publicKeyRingFromSecretKeyRing(secretKeys));
return new OpenPgpV4Fingerprint(secretKeys);
}
/*
Private stuff.
*/
private void processPublicKeysListElement(BareJid contact, PublicKeysListElement listElement) {
OpenPgpContact openPgpContact = getOpenPgpContact(contact.asEntityBareJidIfPossible());
try {
openPgpContact.updateKeys(connection(), listElement);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Could not update contacts keys", e);
}
}
/**
* Decrypt and or verify an {@link OpenPgpElement} and return the decrypted {@link OpenPgpMessage}.
*
* @param element {@link OpenPgpElement} containing the message.
* @param sender {@link OpenPgpContact} who sent the message.
*
* @return decrypted and/or verified message
*
* @throws SmackException.NotLoggedInException in case we aren't logged in (we need to know our jid)
* @throws IOException IO error (reading keys, streams etc)
* @throws PGPException in case of an PGP error
*/
public OpenPgpMessage decryptOpenPgpElement(OpenPgpElement element, OpenPgpContact sender)
throws SmackException.NotLoggedInException, IOException, PGPException {
return provider.decryptAndOrVerify(getAuthenticatedConnectionOrThrow(), element, getOpenPgpSelf(), sender);
}
private void incomingChatMessageListener(final EntityBareJid from, final Message message, Chat chat) {
Async.go(new Runnable() {
@Override
public void run() {
OpenPgpElement element = message.getExtension(OpenPgpElement.class);
if (element == null) {
// Message does not contain an OpenPgpElement -> discard
return;
}
OpenPgpContact contact = getOpenPgpContact(from);
OpenPgpMessage decrypted = null;
OpenPgpContentElement contentElement = null;
try {
decrypted = decryptOpenPgpElement(element, contact);
contentElement = decrypted.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 (SmackException.NotLoggedInException e) {
LOGGER.log(Level.WARNING, "Cannot determine our JID, since we are not logged in.", e);
}
if (contentElement instanceof SigncryptElement) {
for (SigncryptElementReceivedListener l : signcryptElementReceivedListeners) {
l.signcryptElementReceived(contact, message, (SigncryptElement) contentElement,
decrypted.getMetadata());
}
return;
}
if (contentElement instanceof SignElement) {
for (SignElementReceivedListener l : signElementReceivedListeners) {
l.signElementReceived(contact, message, (SignElement) contentElement, decrypted.getMetadata());
}
return;
}
if (contentElement instanceof CryptElement) {
for (CryptElementReceivedListener l : cryptElementReceivedListeners) {
l.cryptElementReceived(contact, message, (CryptElement) contentElement,
decrypted.getMetadata());
}
return;
}
else {
throw new AssertionError("Invalid element received: " + contentElement.getClass().getName());
}
}
});
}
/**
* Create a {@link PubkeyElement} which contains the OpenPGP public key of {@code owner} which belongs to
* the {@link OpenPgpV4Fingerprint} {@code fingerprint}.
*
* @param owner owner of the public key
* @param fingerprint fingerprint of the key
* @param date date of creation of the element
* @return {@link PubkeyElement} containing the key
*
* @throws MissingOpenPgpKeyException if the public key notated by the fingerprint cannot be found
*/
private PubkeyElement createPubkeyElement(BareJid owner,
OpenPgpV4Fingerprint fingerprint,
Date date)
throws MissingOpenPgpKeyException, IOException, PGPException {
PGPPublicKeyRing ring = provider.getStore().getPublicKeyRing(owner, fingerprint);
if (ring != null) {
byte[] keyBytes = ring.getEncoded(true);
return createPubkeyElement(keyBytes, date);
}
throw new MissingOpenPgpKeyException(owner, fingerprint);
}
/**
* Create a {@link PubkeyElement} which contains the given {@code data} base64 encoded.
*
* @param bytes byte representation of an OpenPGP public key
* @param date date of creation of the element
* @return {@link PubkeyElement} containing the key
*/
private static PubkeyElement createPubkeyElement(byte[] bytes, Date date) {
String base64EncodedOpenPgpPubKey = Base64.encodeToString(bytes);
return new PubkeyElement(new PubkeyElement.PubkeyDataElement(base64EncodedOpenPgpPubKey), date);
}
/**
* Register a {@link SigncryptElementReceivedListener} on the {@link OpenPgpManager}.
* That listener will get informed whenever a {@link SigncryptElement} has been received and successfully decrypted.
*
* Note: This method is not intended for clients to listen for incoming {@link SigncryptElement}s.
* Instead its purpose is to allow easy extension of XEP-0373 for custom OpenPGP profiles such as
* OpenPGP for XMPP: Instant Messaging.
*
* @param listener listener that gets registered
*/
public void registerSigncryptReceivedListener(SigncryptElementReceivedListener listener) {
signcryptElementReceivedListeners.add(listener);
}
/**
* Unregister a prior registered {@link SigncryptElementReceivedListener}. That listener will no longer get
* informed about incoming decrypted {@link SigncryptElement}s.
*
* @param listener listener that gets unregistered
*/
void unregisterSigncryptElementReceivedListener(SigncryptElementReceivedListener listener) {
signcryptElementReceivedListeners.remove(listener);
}
/**
* Register a {@link SignElementReceivedListener} on the {@link OpenPgpManager}.
* That listener will get informed whenever a {@link SignElement} has been received and successfully verified.
*
* Note: This method is not intended for clients to listen for incoming {@link SignElement}s.
* Instead its purpose is to allow easy extension of XEP-0373 for custom OpenPGP profiles such as
* OpenPGP for XMPP: Instant Messaging.
*
* @param listener listener that gets registered
*/
void registerSignElementReceivedListener(SignElementReceivedListener listener) {
signElementReceivedListeners.add(listener);
}
/**
* Unregister a prior registered {@link SignElementReceivedListener}. That listener will no longer get
* informed about incoming decrypted {@link SignElement}s.
*
* @param listener listener that gets unregistered
*/
void unregisterSignElementReceivedListener(SignElementReceivedListener listener) {
signElementReceivedListeners.remove(listener);
}
/**
* Register a {@link CryptElementReceivedListener} on the {@link OpenPgpManager}.
* That listener will get informed whenever a {@link CryptElement} has been received and successfully decrypted.
*
* Note: This method is not intended for clients to listen for incoming {@link CryptElement}s.
* Instead its purpose is to allow easy extension of XEP-0373 for custom OpenPGP profiles such as
* OpenPGP for XMPP: Instant Messaging.
*
* @param listener listener that gets registered
*/
void registerCryptElementReceivedListener(CryptElementReceivedListener listener) {
cryptElementReceivedListeners.add(listener);
}
/**
* Unregister a prior registered {@link CryptElementReceivedListener}. That listener will no longer get
* informed about incoming decrypted {@link CryptElement}s.
*
* @param listener listener that gets unregistered
*/
void unregisterCryptElementReceivedListener(CryptElementReceivedListener listener) {
cryptElementReceivedListeners.remove(listener);
}
/**
* Throw an {@link IllegalStateException} if no {@link OpenPgpProvider} is set.
* The OpenPgpProvider is used to process information related to RFC-4880.
*/
private void throwIfNoProviderSet() {
if (provider == null) {
throw new IllegalStateException("No OpenPgpProvider set!");
}
}
/**
* Throw a {@link org.jivesoftware.smack.SmackException.NotLoggedInException} if the {@link XMPPConnection} of this
* manager is not authenticated at this point.
*
* @throws SmackException.NotLoggedInException if we are not authenticated
*/
private void throwIfNotAuthenticated() throws SmackException.NotLoggedInException {
if (!connection().isAuthenticated()) {
throw new SmackException.NotLoggedInException();
}
}
}