Smack/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpContact.java

182 lines
7.4 KiB
Java
Raw Normal View History

2018-05-30 22:06:09 +02:00
/**
*
* 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.
*/
2018-06-13 18:39:09 +02:00
package org.jivesoftware.smackx.ox.chat;
2018-05-30 22:06:09 +02:00
2018-06-13 18:39:09 +02:00
import java.io.IOException;
2018-05-30 22:06:09 +02:00
import java.util.Collections;
import java.util.List;
import org.jivesoftware.smack.SmackException;
2018-06-20 12:45:05 +02:00
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.chat2.ChatManager;
2018-05-30 22:06:09 +02:00
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
2018-06-13 18:39:09 +02:00
import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smack.util.stringencoder.Base64;
2018-05-30 22:06:09 +02:00
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
import org.jivesoftware.smackx.hints.element.StoreHint;
2018-06-13 18:39:09 +02:00
import org.jivesoftware.smackx.ox.OpenPgpProvider;
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
2018-06-20 11:02:30 +02:00
import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
2018-05-30 22:06:09 +02:00
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.MissingOpenPgpPublicKeyException;
2018-06-13 18:39:09 +02:00
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
import org.jivesoftware.smackx.ox.listener.internal.FingerprintsChangedListener;
2018-06-20 11:02:30 +02:00
import org.jivesoftware.smackx.ox.util.DecryptedBytesAndMetadata;
2018-05-30 22:06:09 +02:00
2018-06-13 18:39:09 +02:00
import org.jxmpp.jid.BareJid;
2018-05-30 22:06:09 +02:00
import org.jxmpp.jid.Jid;
2018-06-20 11:02:30 +02:00
import org.xmlpull.v1.XmlPullParserException;
2018-05-30 22:06:09 +02:00
public class OpenPgpContact implements FingerprintsChangedListener {
2018-05-30 22:06:09 +02:00
2018-06-20 12:45:05 +02:00
private final BareJid jid;
private OpenPgpFingerprints contactsFingerprints;
private OpenPgpFingerprints ourFingerprints;
2018-05-30 22:06:09 +02:00
private final OpenPgpProvider cryptoProvider;
private final OpenPgpV4Fingerprint singingKey;
2018-06-20 11:02:30 +02:00
public OpenPgpContact(OpenPgpProvider cryptoProvider,
2018-06-20 12:45:05 +02:00
BareJid jid,
2018-06-20 11:02:30 +02:00
OpenPgpFingerprints ourFingerprints,
OpenPgpFingerprints contactsFingerprints) {
2018-05-30 22:06:09 +02:00
this.cryptoProvider = cryptoProvider;
2018-06-20 12:45:05 +02:00
this.jid = jid;
this.singingKey = cryptoProvider.getStore().getSigningKeyPairFingerprint();
2018-05-30 22:06:09 +02:00
this.ourFingerprints = ourFingerprints;
this.contactsFingerprints = contactsFingerprints;
}
2018-06-20 12:45:05 +02:00
public BareJid getJid() {
return jid;
2018-06-20 11:02:30 +02:00
}
2018-06-20 12:45:05 +02:00
public OpenPgpFingerprints getFingerprints() {
2018-06-20 11:02:30 +02:00
return contactsFingerprints;
}
2018-06-21 15:20:04 +02:00
public OpenPgpElement encryptAndSign(List<ExtensionElement> payload)
2018-06-20 12:45:05 +02:00
throws IOException, SmackOpenPgpException, MissingOpenPgpKeyPairException {
MultiMap<BareJid, OpenPgpV4Fingerprint> fingerprints = oursAndRecipientFingerprints();
SigncryptElement preparedPayload = new SigncryptElement(
Collections.<Jid>singleton(getJid()),
payload);
byte[] encryptedBytes;
// Encrypt the payload
try {
encryptedBytes = cryptoProvider.signAndEncrypt(
preparedPayload,
singingKey,
fingerprints);
} catch (MissingOpenPgpPublicKeyException e) {
throw new AssertionError("Missing OpenPGP public key, even though this should not happen here.", e);
}
2018-06-21 15:20:04 +02:00
return new OpenPgpElement(Base64.encodeToString(encryptedBytes));
}
public void addSignedEncryptedPayloadTo(Message message, List<ExtensionElement> payload)
throws IOException, SmackOpenPgpException, MissingOpenPgpKeyPairException {
2018-06-20 12:45:05 +02:00
// Add encrypted payload to message
2018-06-21 15:20:04 +02:00
OpenPgpElement encryptedPayload = encryptAndSign(payload);
2018-06-20 12:45:05 +02:00
message.addExtension(encryptedPayload);
// Add additional information to the message
if (!ExplicitMessageEncryptionElement.hasProtocol(message,
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.openpgpV0)) {
message.addExtension(new ExplicitMessageEncryptionElement(
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.openpgpV0));
}
2018-06-20 12:45:05 +02:00
StoreHint.set(message);
message.setBody("This message is encrypted using XEP-0374: OpenPGP for XMPP: Instant Messaging.");
}
public void send(XMPPConnection connection, Message message, List<ExtensionElement> payload)
2018-06-13 18:39:09 +02:00
throws MissingOpenPgpKeyPairException, SmackException.NotConnectedException, InterruptedException,
SmackOpenPgpException, IOException {
MultiMap<BareJid, OpenPgpV4Fingerprint> fingerprints = oursAndRecipientFingerprints();
2018-05-30 22:06:09 +02:00
SigncryptElement preparedPayload = new SigncryptElement(
2018-06-20 12:45:05 +02:00
Collections.<Jid>singleton(getJid()),
2018-05-30 22:06:09 +02:00
payload);
2018-06-13 18:39:09 +02:00
2018-05-30 22:06:09 +02:00
OpenPgpElement encryptedPayload;
2018-06-13 18:39:09 +02:00
byte[] encryptedMessage;
2018-05-30 22:06:09 +02:00
// Encrypt the payload
try {
2018-06-13 18:39:09 +02:00
encryptedMessage = cryptoProvider.signAndEncrypt(
2018-05-30 22:06:09 +02:00
preparedPayload,
singingKey,
2018-06-13 18:39:09 +02:00
fingerprints);
2018-05-30 22:06:09 +02:00
} catch (MissingOpenPgpPublicKeyException e) {
throw new AssertionError("Missing OpenPGP public key, even though this should not happen here.", e);
}
2018-06-13 18:39:09 +02:00
encryptedPayload = new OpenPgpElement(Base64.encodeToString(encryptedMessage));
2018-05-30 22:06:09 +02:00
// Add encrypted payload to message
message.addExtension(encryptedPayload);
2018-06-20 12:45:05 +02:00
ChatManager.getInstanceFor(connection).chatWith(getJid().asEntityBareJidIfPossible()).send(message);
2018-05-30 22:06:09 +02:00
}
2018-06-20 11:02:30 +02:00
public OpenPgpContentElement receive(OpenPgpElement element)
throws XmlPullParserException, MissingOpenPgpKeyPairException, SmackOpenPgpException, IOException {
byte[] decoded = Base64.decode(element.getEncryptedBase64MessageContent());
2018-06-20 12:45:05 +02:00
DecryptedBytesAndMetadata decryptedBytes = cryptoProvider.decrypt(decoded, getJid(), null);
2018-06-20 11:02:30 +02:00
OpenPgpMessage openPgpMessage = new OpenPgpMessage(decryptedBytes.getBytes(),
new OpenPgpMessage.Metadata(decryptedBytes.getDecryptionKey(),
decryptedBytes.getVerifiedSignatures()));
return openPgpMessage.getOpenPgpContentElement();
}
2018-06-13 18:39:09 +02:00
private MultiMap<BareJid, OpenPgpV4Fingerprint> oursAndRecipientFingerprints() {
MultiMap<BareJid, OpenPgpV4Fingerprint> 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;
}
}
2018-05-30 22:06:09 +02:00
}