diff --git a/smack-core/src/main/resources/org.jivesoftware.smack/smack-config.xml b/smack-core/src/main/resources/org.jivesoftware.smack/smack-config.xml
index 8ce5b7188..e1be746e0 100644
--- a/smack-core/src/main/resources/org.jivesoftware.smack/smack-config.xml
+++ b/smack-core/src/main/resources/org.jivesoftware.smack/smack-config.xml
@@ -21,6 +21,6 @@
org.jivesoftware.smack.java7.Java7SmackInitializer
org.jivesoftware.smack.im.SmackImInitializer
org.jivesoftware.smackx.omemo.OmemoInitializer
- org.jivesoftware.smackx.ox.OpenPgpInitializer
+ org.jivesoftware.smackx.ox.util.OpenPgpInitializer
diff --git a/smack-openpgp-bouncycastle/build.gradle b/smack-openpgp-bouncycastle/build.gradle
index 988ae4d77..3a66cd602 100644
--- a/smack-openpgp-bouncycastle/build.gradle
+++ b/smack-openpgp-bouncycastle/build.gradle
@@ -7,7 +7,7 @@ dependencies {
compile project(':smack-core')
compile project(':smack-openpgp')
compile 'org.bouncycastle:bcpg-jdk15on:1.59'
- compile 'de.vanitasvitae.pgpainless:pgpainless:2.1.0'
+ compile 'de.vanitasvitae.crypto:pgpainless:0.1-SNAPSHOT'
testCompile project(path: ":smack-core", configuration: "testRuntime")
testCompile project(path: ":smack-core", configuration: "archives")
testCompile project(path: ":smack-openpgp", configuration: "testRuntime")
diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/AbstractPainlessOpenPgpStore.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/AbstractPainlessOpenPgpStore.java
new file mode 100644
index 000000000..f6c3742ca
--- /dev/null
+++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/AbstractPainlessOpenPgpStore.java
@@ -0,0 +1,62 @@
+package org.jivesoftware.smackx.ox.bouncycastle;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
+import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
+import org.jxmpp.jid.BareJid;
+
+public abstract class AbstractPainlessOpenPgpStore implements PainlessOpenPgpStore {
+
+ private final BcKeyFingerprintCalculator fingerprintCalculator = new BcKeyFingerprintCalculator();
+
+ private final Map publicKeyRings = new HashMap<>();
+ private final Map secretKeyRings = new HashMap<>();
+
+ @Override
+ public PGPPublicKeyRingCollection getPublicKeyRings(BareJid owner) throws IOException, PGPException {
+ PGPPublicKeyRingCollection keyRing = publicKeyRings.get(owner);
+ if (keyRing != null) {
+ return keyRing;
+ }
+
+ byte[] bytes = loadPublicKeyRingBytes(owner);
+ keyRing = new PGPPublicKeyRingCollection(bytes, fingerprintCalculator);
+
+ publicKeyRings.put(owner, keyRing);
+
+ return keyRing;
+ }
+
+ @Override
+ public PGPSecretKeyRingCollection getSecretKeyRing(BareJid owner) throws IOException, PGPException {
+ PGPSecretKeyRingCollection keyRing = secretKeyRings.get(owner);
+ if (keyRing != null) {
+ return keyRing;
+ }
+
+ byte[] bytes = loadSecretKeyRingBytes(owner);
+ keyRing = new PGPSecretKeyRingCollection(bytes, fingerprintCalculator);
+
+ secretKeyRings.put(owner, keyRing);
+
+ return keyRing;
+ }
+
+ @Override
+ public void storePublicKeyRing(BareJid owner, PGPPublicKeyRingCollection publicKeys) throws IOException {
+ publicKeyRings.put(owner, publicKeys);
+ storePublicKeyRingBytes(owner, publicKeys.getEncoded());
+ }
+
+ @Override
+ public void storeSecretKeyRing(BareJid owner, PGPSecretKeyRingCollection secretKeys) throws IOException {
+ secretKeyRings.put(owner, secretKeys);
+ storeSecretKeyRingBytes(owner, secretKeys.getEncoded());
+ }
+
+}
diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpProvider.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpProvider.java
deleted file mode 100644
index f81b382e9..000000000
--- a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpProvider.java
+++ /dev/null
@@ -1,231 +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.bouncycastle;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.Charset;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.util.Date;
-import java.util.Set;
-
-import org.jivesoftware.smack.XMPPConnection;
-import org.jivesoftware.smack.util.stringencoder.Base64;
-import org.jivesoftware.smackx.ox.OpenPgpMessage;
-import org.jivesoftware.smackx.ox.OpenPgpProvider;
-import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
-import org.jivesoftware.smackx.ox.callback.SecretKeyRestoreSelectionCallback;
-import org.jivesoftware.smackx.ox.element.CryptElement;
-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.SmackOpenPgpException;
-
-import name.neuhalfen.projects.crypto.bouncycastle.openpgp.BouncyGPG;
-import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PublicKeySize;
-import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.XmppKeySelectionStrategy;
-import org.bouncycastle.openpgp.PGPException;
-import org.bouncycastle.openpgp.PGPKeyRingGenerator;
-import org.bouncycastle.openpgp.PGPPublicKey;
-import org.bouncycastle.util.encoders.Hex;
-import org.bouncycastle.util.io.Streams;
-import org.jxmpp.jid.BareJid;
-
-public class BCOpenPgpProvider implements OpenPgpProvider {
-
- private final BareJid user;
-
- private BCOpenPgpStore store;
-
-
- public BCOpenPgpProvider(BareJid user) {
- this.user = user;
- }
-
- public void setStore(BCOpenPgpStore store) {
- this.store = store;
- }
-
- @Override
- public OpenPgpV4Fingerprint primaryOpenPgpKeyPairFingerprint() {
- return store.primaryOpenPgpKeyPairFingerprint();
- }
-
- @Override
- public Set availableOpenPgpKeyPairFingerprints() {
- return store.availableOpenPgpKeyPairFingerprints();
- }
-
- @Override
- public Set announcedOpenPgpKeyFingerprints(BareJid contact) {
- return store.announcedOpenPgpKeyFingerprints(contact);
- }
-
- @Override
- public OpenPgpElement signAndEncrypt(SigncryptElement element, OpenPgpV4Fingerprint signingKey, Set encryptionKeys)
- throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException {
- if (encryptionKeys.isEmpty()) {
- throw new IllegalArgumentException("Set of recipients must not be empty");
- }
-
- encryptionKeys.addAll(store.announcedOpenPgpKeyFingerprints(user));
- long[] recipientIds = new long[encryptionKeys.size()];
- int pos = 0;
- for (OpenPgpV4Fingerprint f : encryptionKeys) {
- recipientIds[pos++] = f.getKeyId();
- }
-
- InputStream inputStream = element.toInputStream();
- ByteArrayOutputStream encryptedOut = new ByteArrayOutputStream();
-
- try {
- OutputStream encryptor = BouncyGPG.encryptToStream()
- .withConfig(store.getKeyringConfig())
- .withKeySelectionStrategy(new XmppKeySelectionStrategy(new Date()))
- .withOxAlgorithms()
- .toRecipients(recipientIds)
- .andSignWith(signingKey.getKeyId())
- .binaryOutput()
- .andWriteTo(encryptedOut);
-
- Streams.pipeAll(inputStream, encryptor);
- encryptor.close();
-
- String base64 = Base64.encodeToString(encryptedOut.toByteArray());
-
- return new OpenPgpElement(base64);
- } catch (Exception e) {
- throw new AssertionError(e);
- // TODO: Later
- }
- }
-
- @Override
- public OpenPgpMessage decryptAndVerify(OpenPgpElement element, Set sendersKeys)
- throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException {
-
- ByteArrayInputStream encryptedIn = new ByteArrayInputStream(
- element.getEncryptedBase64MessageContent().getBytes(Charset.forName("UTF-8")));
-
- try {
- InputStream decrypted = BouncyGPG.decryptAndVerifyStream()
- .withConfig(store.getKeyringConfig())
- .withKeySelectionStrategy(new XmppKeySelectionStrategy(new Date()))
- .andValidateSomeoneSigned() // TODO: Validate using sender keys
- .fromEncryptedInputStream(encryptedIn);
-
- ByteArrayOutputStream decryptedOut = new ByteArrayOutputStream();
-
- Streams.pipeAll(decrypted, decryptedOut);
-
- return new OpenPgpMessage(OpenPgpMessage.State.signcrypt, new String(decryptedOut.toByteArray(), Charset.forName("UTF-8")));
- } catch (IOException | NoSuchProviderException e) {
- throw new AssertionError(e);
- }
- }
-
- @Override
- public OpenPgpElement sign(SignElement element, OpenPgpV4Fingerprint singingKeyFingerprint)
- throws MissingOpenPgpKeyPairException {
- throw new AssertionError("Feature not implemented!");
- }
-
- @Override
- public OpenPgpMessage verify(OpenPgpElement element, Set singingKeyFingerprints)
- throws MissingOpenPgpPublicKeyException {
- throw new AssertionError("Feature not implemented!");
- }
-
- @Override
- public OpenPgpElement encrypt(CryptElement element, Set encryptionKeyFingerprints)
- throws MissingOpenPgpPublicKeyException {
- throw new AssertionError("Feature not implemented!");
- }
-
- @Override
- public OpenPgpMessage decrypt(OpenPgpElement element)
- throws MissingOpenPgpKeyPairException {
- throw new AssertionError("Feature not implemented!");
- }
-
- @Override
- public PubkeyElement createPubkeyElement(OpenPgpV4Fingerprint fingerprint)
- throws MissingOpenPgpPublicKeyException, SmackOpenPgpException {
- return store.createPubkeyElement(fingerprint);
- }
-
- @Override
- public void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element)
- throws SmackOpenPgpException {
- store.storePublicKey(owner, fingerprint, element);
- }
-
- @Override
- public void storePublicKeysList(XMPPConnection connection, PublicKeysListElement listElement, BareJid owner) {
- store.storePublicKeysList(connection, listElement, owner);
- }
-
- @Override
- public OpenPgpV4Fingerprint createOpenPgpKeyPair()
- throws NoSuchAlgorithmException, NoSuchProviderException, SmackOpenPgpException {
- return store.createOpenPgpKeyPair();
- }
-
- @Override
- public SecretkeyElement createSecretkeyElement(Set fingerprints, String password)
- throws MissingOpenPgpKeyPairException, SmackOpenPgpException {
- return store.createSecretkeyElement(fingerprints, password);
- }
-
- @Override
- public Set availableOpenPgpPublicKeysFingerprints(BareJid contact)
- throws SmackOpenPgpException {
- return store.availableOpenPgpPublicKeysFingerprints(contact);
- }
-
- @Override
- public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback)
- throws SmackOpenPgpException, InvalidBackupCodeException {
- store.restoreSecretKeyBackup(secretkeyElement, password, callback);
- }
-
- static PGPKeyRingGenerator generateKey(BareJid owner)
- throws NoSuchAlgorithmException, PGPException, NoSuchProviderException {
- PGPKeyRingGenerator generator = BouncyGPG.createKeyPair()
- .withRSAKeys()
- .ofSize(PublicKeySize.RSA._2048)
- .forIdentity("xmpp:" + owner.toString())
- .withoutPassphrase()
- .build();
- return generator;
- }
-
- public static OpenPgpV4Fingerprint getFingerprint(PGPPublicKey publicKey) {
- byte[] hex = Hex.encode(publicKey.getFingerprint());
- return new OpenPgpV4Fingerprint(hex);
- }
-}
diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedBcOpenPgpStore.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedBcOpenPgpStore.java
deleted file mode 100644
index 2118182ce..000000000
--- a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedBcOpenPgpStore.java
+++ /dev/null
@@ -1,567 +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.bouncycastle;
-
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.text.ParseException;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.jivesoftware.smack.XMPPConnection;
-import org.jivesoftware.smack.util.stringencoder.Base64;
-import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
-import org.jivesoftware.smackx.ox.Util;
-import org.jivesoftware.smackx.ox.callback.SecretKeyRestoreSelectionCallback;
-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.exception.InvalidBackupCodeException;
-import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
-import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
-import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
-
-import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PGPHashAlgorithms;
-import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PGPSymmetricEncryptionAlgorithms;
-import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.KeyringConfigCallback;
-import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.KeyringConfigCallbacks;
-import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.InMemoryKeyring;
-import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfig;
-import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfigs;
-import org.bouncycastle.bcpg.HashAlgorithmTags;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openpgp.PGPException;
-import org.bouncycastle.openpgp.PGPPrivateKey;
-import org.bouncycastle.openpgp.PGPPublicKey;
-import org.bouncycastle.openpgp.PGPPublicKeyRing;
-import org.bouncycastle.openpgp.PGPSecretKey;
-import org.bouncycastle.openpgp.PGPSecretKeyRing;
-import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
-import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
-import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
-import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
-import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
-import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
-import org.jxmpp.jid.BareJid;
-import org.jxmpp.util.XmppDateTime;
-
-public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
-
- private static final Logger LOGGER = Logger.getLogger(FileBasedBcOpenPgpStore.class.getName());
-
- private final File basePath;
- private final BareJid user;
- private final InMemoryKeyring keyringConfig;
- private final KeyringConfigCallback configCallback;
- private OpenPgpV4Fingerprint primaryKeyFingerprint;
-
- public FileBasedBcOpenPgpStore(File basePath, BareJid user, KeyringConfigCallback passwordCallback)
- throws IOException, PGPException {
- this.basePath = basePath;
- this.user = user;
-
- File pub = publicKeyringPath();
- if (!pub.exists()) {
- pub.getParentFile().mkdirs();
- pub.createNewFile();
- }
-
- File sec = secretKeyringPath();
- if (!sec.exists()) {
- sec.createNewFile();
- }
-
- configCallback = passwordCallback;
- keyringConfig = KeyringConfigs.forGpgExportedKeys(configCallback);
-
- addPublicKeysFromFile(keyringConfig, pub, configCallback);
- PGPPublicKey lastAdded = addSecretKeysFromFile(keyringConfig, sec, configCallback);
-
- if (lastAdded != null) {
- primaryKeyFingerprint = BCOpenPgpProvider.getFingerprint(lastAdded);
- }
- }
-
- @Override
- public OpenPgpV4Fingerprint primaryOpenPgpKeyPairFingerprint() {
- return primaryKeyFingerprint;
- }
-
- @Override
- public Set availableOpenPgpKeyPairFingerprints() {
- Set availableKeyPairs = new HashSet<>();
- try {
- for (PGPSecretKeyRing secRing : keyringConfig.getSecretKeyRings()) {
- for (PGPSecretKey secKey : secRing) {
- availableKeyPairs.add(BCOpenPgpProvider.getFingerprint(secKey.getPublicKey()));
- }
- }
- } catch (IOException | PGPException e) {
- LOGGER.log(Level.SEVERE, "Error going through available key pairs.", e);
- }
- return availableKeyPairs;
- }
-
- @Override
- public Map announcedOpenPgpKeyFingerprints(BareJid contact) {
- Map announcedKeys = new HashMap<>();
- File listPath = contactsList(contact);
- if (listPath.exists() && listPath.isFile()) {
- BufferedReader reader = null;
- try {
- reader = new BufferedReader(new InputStreamReader(
- new FileInputStream(listPath), "UTF8"));
-
- String line;
- while ((line = reader.readLine()) != null) {
- line = line.trim();
- if (line.isEmpty()) {
- continue;
- }
-
- String[] split = line.split(" ");
-
- OpenPgpV4Fingerprint fingerprint;
- Date date = null;
- try {
- fingerprint = new OpenPgpV4Fingerprint(split[0]);
- } catch (IllegalArgumentException e) {
- LOGGER.log(Level.INFO, "Skip malformed fingerprint " + line + " of " + contact.toString());
- continue;
- }
-
- try {
- if (split.length > 1)
- date = XmppDateTime.parseXEP0082Date(split[1]);
- }
- catch (ParseException e) {
- LOGGER.log(Level.WARNING, "Could not parse date", e);
- }
- announcedKeys.put(fingerprint, date);
- }
- reader.close();
- } catch (IOException e) {
- if (reader != null) {
- try {
- reader.close();
- } catch (IOException e1) {
- // Ignore
- }
- }
- }
- }
- return announcedKeys;
- }
-
- @Override
- public Set availableOpenPgpPublicKeysFingerprints(BareJid contact)
- throws SmackOpenPgpException {
- Set availableKeys = new HashSet<>();
- try {
- Iterator ringIterator = keyringConfig.getPublicKeyRings().getKeyRings("xmpp:" + contact.toString());
- while (ringIterator.hasNext()) {
- PGPPublicKeyRing ring = ringIterator.next();
- Iterator keyIterator = ring.getPublicKeys();
- while (keyIterator.hasNext()) {
- PGPPublicKey key = keyIterator.next();
- if (key.isEncryptionKey()) {
- availableKeys.add(BCOpenPgpProvider.getFingerprint(key));
- }
- }
- }
- } catch (PGPException | IOException e) {
- throw new SmackOpenPgpException(e);
- }
- return availableKeys;
- }
-
- @Override
- public void storePublicKeysList(XMPPConnection connection, PublicKeysListElement listElement, BareJid owner) {
- File listPath = contactsList(owner);
- try {
- if (!listPath.exists()) {
- listPath.getParentFile().mkdirs();
- listPath.createNewFile();
- BufferedWriter writer = null;
- try {
- writer = new BufferedWriter(new OutputStreamWriter(
- new FileOutputStream(listPath), "UTF8"));
-
- for (PublicKeysListElement.PubkeyMetadataElement entry : listElement.getMetadata().values()) {
- OpenPgpV4Fingerprint fingerprint = entry.getV4Fingerprint();
- Date date = entry.getDate();
- String line = fingerprint.toString() + (date != null ? date : "");
- writer.write(line);
- writer.newLine();
- }
-
- writer.close();
-
- } catch (IOException e) {
- if (writer != null) {
- writer.close();
- }
- }
- }
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, "Error writing list of announced keys for " + owner.toString(), e);
- }
- }
-
- @Override
- public PubkeyElement createPubkeyElement(OpenPgpV4Fingerprint fingerprint)
- throws MissingOpenPgpPublicKeyException, SmackOpenPgpException {
- try {
- PGPPublicKey publicKey = keyringConfig.getPublicKeyRings().getPublicKey(Util.keyIdFromFingerprint(fingerprint));
- if (publicKey == null) {
- throw new MissingOpenPgpPublicKeyException(user, fingerprint);
- }
- byte[] base64 = Base64.encode(publicKey.getEncoded());
- return new PubkeyElement(new PubkeyElement.PubkeyDataElement(base64), new Date());
- } catch (PGPException | IOException e) {
- throw new SmackOpenPgpException(e);
- }
- }
-
- @Override
- public void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element, Date latestMetadataDate)
- throws SmackOpenPgpException {
- byte[] base64decoded = Base64.decode(element.getDataElement().getB64Data());
- try {
- keyringConfig.addPublicKey(base64decoded);
- writePublicKeysToFile(keyringConfig, publicKeyringPath());
- } catch (PGPException | IOException e) {
- throw new SmackOpenPgpException(e);
- } catch (IllegalArgumentException e) {
- LOGGER.log(Level.WARNING, "Public Key with ID " + fingerprint.toString() + " of " +
- owner + " is already in memory. Skip.");
- return;
- }
-
- try {
- writeDateToFile(publicKeyUpdateDatePath(owner, fingerprint), latestMetadataDate);
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, "Could not store update date for " + fingerprint.toString(), e);
- }
- }
-
- @Override
- public SecretkeyElement createSecretkeyElement(Set fingerprints, String password)
- throws MissingOpenPgpKeyPairException, SmackOpenPgpException {
-
- PGPDigestCalculator calculator;
- try {
- calculator = new JcaPGPDigestCalculatorProviderBuilder()
- .setProvider(BouncyCastleProvider.PROVIDER_NAME)
- .build()
- .get(HashAlgorithmTags.SHA1);
- } catch (PGPException e) {
- throw new AssertionError(e);
- }
-
- PBESecretKeyEncryptor encryptor = new JcePBESecretKeyEncryptorBuilder(
- PGPSymmetricEncryptionAlgorithms.AES_256.getAlgorithmId())
- .setProvider(BouncyCastleProvider.PROVIDER_NAME)
- .build(password.toCharArray());
-
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-
- try {
- for (OpenPgpV4Fingerprint fingerprint : fingerprints) {
- // Our unencrypted secret key
- PGPSecretKey secretKey = keyringConfig.getSecretKeyRings()
- .getSecretKey(Util.keyIdFromFingerprint(fingerprint));
-
- if (secretKey == null) {
- buffer.close();
- throw new MissingOpenPgpKeyPairException(user);
- }
-
- PGPSecretKey encrypted = new PGPSecretKey(
- secretKey.extractPrivateKey(null),
- secretKey.getPublicKey(),
- calculator,
- true,
- encryptor);
-
- buffer.write(encrypted.getEncoded());
- }
-
- return new SecretkeyElement(Base64.encode(buffer.toByteArray()));
-
- } catch (PGPException | IOException e) {
- throw new SmackOpenPgpException(e);
- }
- }
-
- @Override
- public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback)
- throws SmackOpenPgpException, InvalidBackupCodeException { // TODO: Figure out InvalidBackupCodeException
- byte[] base64Decoded = Base64.decode(secretkeyElement.getB64Data());
-
- try {
- PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder()
- .setProvider(BouncyCastleProvider.PROVIDER_NAME)
- .build();
-
- ByteArrayInputStream inputStream = new ByteArrayInputStream(base64Decoded);
- KeyringConfig keyring = KeyringConfigs.withKeyRingsFromStreams(null, inputStream,
- KeyringConfigCallbacks.withPassword(password));
-
- Map availableKeys = new HashMap<>();
- OpenPgpV4Fingerprint selectedKey;
-
- for (PGPSecretKeyRing r : keyring.getSecretKeyRings()) {
- PGPSecretKey s = r.getSecretKey();
- PGPPrivateKey privateKey = s.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder(calculatorProvider).build(password.toCharArray()));
- PGPPublicKey publicKey = s.getPublicKey();
- PGPSecretKey secretKey = new PGPSecretKey(
- privateKey,
- publicKey,
- calculatorProvider.get(PGPHashAlgorithms.SHA1.getAlgorithmId()),
- true,
- null);
- availableKeys.put(BCOpenPgpProvider.getFingerprint(publicKey), secretKey);
- }
-
- selectedKey = callback.selectSecretKeyToRestore(availableKeys.keySet());
- if (selectedKey != null) {
- try {
- keyringConfig.addSecretKey(availableKeys.get(selectedKey).getEncoded());
- } catch (IllegalArgumentException e) {
- LOGGER.log(Level.INFO, "Users secret key " + selectedKey.toString() + " is already in keyring. Skip.");
- }
-
- try {
- keyringConfig.addPublicKey(availableKeys.get(selectedKey).getPublicKey().getEncoded());
- } catch (IllegalArgumentException e) {
- LOGGER.log(Level.INFO, "Users public key " + selectedKey.toString() + " is already in keyring. Skip.");
- }
- primaryKeyFingerprint = selectedKey;
- writePrivateKeysToFile(keyringConfig, secretKeyringPath());
- writePublicKeysToFile(keyring, publicKeyringPath());
- }
- } catch (PGPException | IOException e) {
- throw new SmackOpenPgpException(e);
- }
- }
-
- @Override
- public OpenPgpV4Fingerprint createOpenPgpKeyPair()
- throws NoSuchAlgorithmException, NoSuchProviderException, SmackOpenPgpException {
- try {
- PGPSecretKeyRing ourKey = BCOpenPgpProvider.generateKey(user).generateSecretKeyRing();
- keyringConfig.addSecretKey(ourKey.getSecretKey().getEncoded());
- keyringConfig.addPublicKey(ourKey.getPublicKey().getEncoded());
- writePrivateKeysToFile(keyringConfig, secretKeyringPath());
- writePublicKeysToFile(keyringConfig, publicKeyringPath());
- primaryKeyFingerprint = BCOpenPgpProvider.getFingerprint(ourKey.getPublicKey());
- return primaryKeyFingerprint;
- } catch (PGPException | IOException e) {
- throw new SmackOpenPgpException(e);
- }
- }
-
- @Override
- public Date getPubkeysLatestUpdateDate(BareJid owner, OpenPgpV4Fingerprint fingerprint) {
- return readDateFromFile(publicKeyUpdateDatePath(owner, fingerprint));
- }
-
- private File secretKeyringPath() {
- return new File(contactsPath(user), "secring.skr");
- }
-
- private File publicKeyringPath() {
- return new File(contactsPath(user), "pubring.pkr");
- }
-
- private File contactsPath() {
- return new File(basePath, user.toString() + "/users");
- }
-
- private File contactsPath(BareJid contact) {
- return new File(contactsPath(), contact.toString());
- }
-
- private File contactsList(BareJid contact) {
- return new File(contactsPath(contact), "metadata.list");
- }
-
- private File publicKeyUpdateDatePath(BareJid owner, OpenPgpV4Fingerprint fingerprint) {
- return new File(contactsPath(owner), fingerprint.toString() + "-update.date");
- }
-
- private static void writeDateToFile(File file, Date date) throws IOException {
- if (!file.exists()) {
- file.getParentFile().mkdirs();
- file.createNewFile();
- }
-
- BufferedWriter writer = null;
- try {
- writer = new BufferedWriter(new OutputStreamWriter(
- new FileOutputStream(file), "UTF8"));
- writer.write(XmppDateTime.formatXEP0082Date(date));
- writer.flush();
- writer.close();
- } catch (IOException e) {
- if (writer != null) {
- writer.close();
- }
- throw e;
- }
- }
-
- private static Date readDateFromFile(File file) {
- if (!file.exists()) {
- return null;
- }
-
- BufferedReader reader = null;
- Date result = null;
- try {
- reader = new BufferedReader(new InputStreamReader(
- new FileInputStream(file), "UTF8"));
- String line = reader.readLine();
- if (!line.isEmpty()) {
- result = XmppDateTime.parseXEP0082Date(line);
- }
- reader.close();
- reader = null;
- } catch (UnsupportedEncodingException | FileNotFoundException e) {
- throw new AssertionError(e);
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, "Exception while reading date.", e);
- } catch (ParseException e) {
- LOGGER.log(Level.WARNING, "Could not parse date", e);
- result = null;
- }
-
- if (reader != null) {
- try {
- reader.close();
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, "Could not close reader.", e);
- }
- }
- return result;
- }
-
- private static void addPublicKeysFromFile(InMemoryKeyring keyring,
- File pubring,
- KeyringConfigCallback passwordCallback)
- throws IOException, PGPException {
- if (!pubring.exists()) {
- return;
- }
-
- InputStream inputStream = new FileInputStream(pubring);
- KeyringConfig source = KeyringConfigs.withKeyRingsFromStreams(inputStream, null, passwordCallback);
- for (PGPPublicKeyRing pubRing : source.getPublicKeyRings()) {
- for (PGPPublicKey pubKey : pubRing) {
- try {
- keyring.addPublicKey(pubKey.getEncoded());
- } catch (IllegalArgumentException e) {
- LOGGER.log(Level.INFO, "public key " + Long.toHexString(pubKey.getKeyID()) +
- " already exists in keyring. Skip.");
- }
-
- }
- }
- }
-
- private static PGPPublicKey addSecretKeysFromFile(InMemoryKeyring keyring,
- File secring,
- KeyringConfigCallback passwordCallback)
- throws IOException, PGPException {
- if (!secring.exists()) {
- return null;
- }
-
- InputStream inputStream = new FileInputStream(secring);
- KeyringConfig source = KeyringConfigs.withKeyRingsFromStreams(null, inputStream, passwordCallback);
- PGPPublicKey lastAdded = null;
-
- for (PGPSecretKeyRing secRing : source.getSecretKeyRings()) {
- for (PGPSecretKey secKey : secRing) {
- keyring.addSecretKey(secKey.getEncoded());
- // Remember last added secret keys public key -> this will be the primary key
- if (secKey.getPublicKey() != null) {
- lastAdded = secKey.getPublicKey();
- }
- }
- }
-
- return lastAdded;
- }
-
- private static void writePublicKeysToFile(KeyringConfig keyring, File pubring)
- throws IOException, PGPException {
- writeBytesToFile(keyring.getPublicKeyRings().getEncoded(), pubring);
- }
-
- private static void writePrivateKeysToFile(KeyringConfig keyring, File secring)
- throws IOException, PGPException {
- writeBytesToFile(keyring.getSecretKeyRings().getEncoded(), secring);
- }
-
- private static void writeBytesToFile(byte[] bytes, File file) throws IOException {
- if (!file.exists()) {
- file.getParentFile().mkdirs();
- file.createNewFile();
- }
-
- FileOutputStream outputStream = null;
- try {
- outputStream = new FileOutputStream(file);
- BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream);
-
- bufferedStream.write(bytes);
- bufferedStream.flush();
- bufferedStream.close();
- } catch (IOException e) {
- if (outputStream != null) {
- outputStream.close();
- }
- }
- }
-
- @Override
- public KeyringConfig getKeyringConfig() {
- return keyringConfig;
- }
-}
diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedOpenPgpStore.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedOpenPgpStore.java
new file mode 100644
index 000000000..0c3b99583
--- /dev/null
+++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedOpenPgpStore.java
@@ -0,0 +1,4 @@
+package org.jivesoftware.smackx.ox.bouncycastle;
+
+public interface FileBasedOpenPgpStore {
+}
diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedPainlessOpenPgpStore.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedPainlessOpenPgpStore.java
new file mode 100644
index 000000000..b593e5243
--- /dev/null
+++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedPainlessOpenPgpStore.java
@@ -0,0 +1,74 @@
+package org.jivesoftware.smackx.ox.bouncycastle;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+
+import org.jivesoftware.smack.util.MultiMap;
+import org.jivesoftware.smackx.ox.OpenPgpStore;
+import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
+import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
+import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
+import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
+
+import org.jxmpp.jid.BareJid;
+
+public class FileBasedPainlessOpenPgpStore implements OpenPgpStore, FileBasedOpenPgpStore {
+
+ @Override
+ public OpenPgpV4Fingerprint getPrimaryOpenPgpKeyPairFingerprint() {
+ return null;
+ }
+
+ @Override
+ public void setPrimaryOpenPgpKeyPairFingerprint(OpenPgpV4Fingerprint fingerprint) {
+
+ }
+
+ @Override
+ public Set getAvailableKeyPairFingerprints() {
+ return null;
+ }
+
+ @Override
+ public Map getAvailableKeysFingerprints(BareJid contact) throws SmackOpenPgpException {
+ return null;
+ }
+
+ @Override
+ public Map getAnnouncedKeysFingerprints(BareJid contact) {
+ return null;
+ }
+
+ @Override
+ public void setAnnouncedKeysFingerprints(BareJid contact, Map fingerprints) {
+
+ }
+
+ @Override
+ public Date getPubkeysLastRevision(BareJid owner, OpenPgpV4Fingerprint fingerprint) {
+ return null;
+ }
+
+ @Override
+ public void setPubkeysLastRevision(BareJid owner, OpenPgpV4Fingerprint fingerprint, Date revision) {
+
+ }
+
+ @Override
+ public MultiMap getAllContactsTrustedFingerprints() {
+ return null;
+ }
+
+ @Override
+ public byte[] getPublicKeyBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
+ throws MissingOpenPgpPublicKeyException {
+ return new byte[0];
+ }
+
+ @Override
+ public byte[] getSecretKeyBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
+ throws MissingOpenPgpKeyPairException {
+ return new byte[0];
+ }
+}
diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/PainlessOpenPgpProvider.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/PainlessOpenPgpProvider.java
new file mode 100644
index 000000000..5ed0a7ca2
--- /dev/null
+++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/PainlessOpenPgpProvider.java
@@ -0,0 +1,256 @@
+package org.jivesoftware.smackx.ox.bouncycastle;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jivesoftware.smack.util.MultiMap;
+import org.jivesoftware.smackx.ox.OpenPgpProvider;
+import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
+import org.jivesoftware.smackx.ox.callback.SmackMissingOpenPgpPublicKeyCallback;
+import org.jivesoftware.smackx.ox.element.CryptElement;
+import org.jivesoftware.smackx.ox.element.SignElement;
+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.util.DecryptedBytesAndMetadata;
+import org.jivesoftware.smackx.ox.util.KeyBytesAndFingerprint;
+
+import de.vanitasvitae.crypto.pgpainless.PGPainless;
+import de.vanitasvitae.crypto.pgpainless.decryption_verification.DecryptionStream;
+import de.vanitasvitae.crypto.pgpainless.decryption_verification.MissingPublicKeyCallback;
+import de.vanitasvitae.crypto.pgpainless.decryption_verification.PainlessResult;
+import de.vanitasvitae.crypto.pgpainless.key.generation.type.length.RsaLength;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSecretKeyRing;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.io.Streams;
+import org.jxmpp.jid.BareJid;
+
+public class PainlessOpenPgpProvider implements OpenPgpProvider {
+
+ private final PainlessOpenPgpStore store;
+ private final BareJid owner;
+
+ public PainlessOpenPgpProvider(BareJid owner, PainlessOpenPgpStore store) {
+ this.owner = owner;
+ this.store = store;
+ }
+
+ @Override
+ public byte[] signAndEncrypt(SigncryptElement element,
+ OpenPgpV4Fingerprint signingKey,
+ MultiMap encryptionKeys)
+ throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException, SmackOpenPgpException,
+ IOException {
+
+ Set allRecipientsKeys = getEncryptionKeys(encryptionKeys);
+ PGPSecretKeyRing signingKeyRing = getSigningKey(signingKey);
+
+ InputStream fromPlain = element.toInputStream();
+ ByteArrayOutputStream encrypted = new ByteArrayOutputStream();
+ OutputStream encryptor;
+ try {
+ encryptor = PGPainless.createEncryptor()
+ .onOutputStream(encrypted)
+ .toRecipients((PGPPublicKeyRing[]) allRecipientsKeys.toArray())
+ .usingSecureAlgorithms()
+ .signWith(store.getSecretKeyProtector(), signingKeyRing)
+ .noArmor();
+ } catch (PGPException e) {
+ throw new SmackOpenPgpException(e);
+ }
+
+ Streams.pipeAll(fromPlain, encryptor);
+ fromPlain.close();
+ encryptor.close();
+
+ return encrypted.toByteArray();
+ }
+
+ @Override
+ public byte[] sign(SignElement element, OpenPgpV4Fingerprint signingKeyFingerprint)
+ throws MissingOpenPgpKeyPairException, IOException, SmackOpenPgpException {
+ InputStream fromPlain = element.toInputStream();
+ PGPSecretKeyRing signingKeyRing;
+ try {
+ signingKeyRing = store.getSecretKeyRing(owner).getSecretKeyRing(signingKeyFingerprint.getKeyId());
+ } catch (PGPException e) {
+ throw new MissingOpenPgpKeyPairException(owner, e);
+ }
+
+ ByteArrayOutputStream toSigned = new ByteArrayOutputStream();
+ OutputStream signer;
+ try {
+ signer = PGPainless.createEncryptor().onOutputStream(toSigned)
+ .doNotEncrypt()
+ .signWith(store.getSecretKeyProtector(), signingKeyRing)
+ .noArmor();
+ } catch (PGPException e) {
+ throw new SmackOpenPgpException(e);
+ }
+
+ Streams.pipeAll(fromPlain, signer);
+
+ fromPlain.close();
+ signer.close();
+
+ return toSigned.toByteArray();
+ }
+
+ @Override
+ public byte[] encrypt(CryptElement element, MultiMap encryptionKeyFingerprints)
+ throws MissingOpenPgpPublicKeyException, IOException, SmackOpenPgpException {
+ Set allRecipientsKeys = getEncryptionKeys(encryptionKeyFingerprints);
+
+ InputStream fromPlain = element.toInputStream();
+ ByteArrayOutputStream encrypted = new ByteArrayOutputStream();
+ OutputStream encryptor;
+ try {
+ encryptor = PGPainless.createEncryptor()
+ .onOutputStream(encrypted)
+ .toRecipients((PGPPublicKeyRing[]) allRecipientsKeys.toArray())
+ .usingSecureAlgorithms()
+ .doNotSign()
+ .noArmor();
+ } catch (PGPException e) {
+ throw new SmackOpenPgpException(e);
+ }
+
+ Streams.pipeAll(fromPlain, encryptor);
+ fromPlain.close();
+ encryptor.close();
+
+ return encrypted.toByteArray();
+ }
+
+ @Override
+ public DecryptedBytesAndMetadata decrypt(byte[] bytes, BareJid sender, final SmackMissingOpenPgpPublicKeyCallback missingPublicKeyCallback)
+ throws MissingOpenPgpKeyPairException, SmackOpenPgpException, IOException {
+ Set trustedKeyIds = new HashSet<>();
+ Set senderKeys = new HashSet<>();
+ InputStream fromEncrypted = new ByteArrayInputStream(bytes);
+ ByteArrayOutputStream toPlain = new ByteArrayOutputStream();
+ DecryptionStream decryptionStream;
+ try {
+ decryptionStream = PGPainless.createDecryptor().onInputStream(fromEncrypted)
+ .decryptWith(store.getSecretKeyRing(owner), store.getSecretKeyProtector())
+ .verifyWith(trustedKeyIds, senderKeys)
+ .handleMissingPublicKeysWith(new MissingPublicKeyCallback() {
+ @Override
+ public void onMissingPublicKeyEncountered(Long aLong) {
+
+ }
+ })
+ .build();
+ } catch (PGPException e) {
+ throw new SmackOpenPgpException(e);
+ }
+ Streams.pipeAll(decryptionStream, toPlain);
+ fromEncrypted.close();
+ decryptionStream.close();
+
+ PainlessResult result = decryptionStream.getResult();
+
+ return new DecryptedBytesAndMetadata(toPlain.toByteArray(),
+ result.getVerifiedSignatureKeyIds(),
+ result.getDecryptionKeyId());
+ }
+
+ @Override
+ public byte[] symmetricallyEncryptWithPassword(byte[] bytes, String password)
+ throws SmackOpenPgpException, IOException {
+ try {
+ return PGPainless.encryptWithPassword(bytes, password.toCharArray());
+ } catch (PGPException e) {
+ throw new SmackOpenPgpException(e);
+ }
+ }
+
+ @Override
+ public byte[] symmetricallyDecryptWithPassword(byte[] bytes, String password)
+ throws SmackOpenPgpException, IOException {
+ try {
+ return PGPainless.decryptWithPassword(bytes, password.toCharArray());
+ } catch (PGPException e) {
+ throw new SmackOpenPgpException(e);
+ }
+ }
+
+ @Override
+ public PainlessOpenPgpStore getStore() {
+ return store;
+ }
+
+ private Set getEncryptionKeys(MultiMap encryptionKeys)
+ throws IOException, SmackOpenPgpException {
+ Set allRecipientsKeys = new HashSet<>();
+
+ for (BareJid recipient : encryptionKeys.keySet()) {
+ PGPPublicKeyRingCollection recipientsKeyRings;
+ try {
+ recipientsKeyRings = store.getPublicKeyRings(recipient);
+ for (OpenPgpV4Fingerprint fingerprint : encryptionKeys.getAll(recipient)) {
+ PGPPublicKeyRing ring = recipientsKeyRings.getPublicKeyRing(fingerprint.getKeyId());
+ if (ring != null) allRecipientsKeys.add(ring);
+ }
+ } catch (PGPException e) {
+ throw new SmackOpenPgpException(e);
+ }
+ }
+
+ return allRecipientsKeys;
+ }
+
+ private PGPSecretKeyRing getSigningKey(OpenPgpV4Fingerprint signingKey)
+ throws IOException, MissingOpenPgpKeyPairException {
+ PGPSecretKeyRing signingKeyRing;
+ try {
+ signingKeyRing = store.getSecretKeyRing(owner).getSecretKeyRing(signingKey.getKeyId());
+ } catch (PGPException e) {
+ throw new MissingOpenPgpKeyPairException(owner, e);
+ }
+ return signingKeyRing;
+ }
+
+ @Override
+ public KeyBytesAndFingerprint generateOpenPgpKeyPair(BareJid owner)
+ throws SmackOpenPgpException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
+ NoSuchProviderException, IOException {
+ PGPSecretKeyRing secretKey;
+ try {
+ secretKey = PGPainless.generateKeyRing().simpleRsaKeyRing("xmpp:" + owner.toString(), RsaLength._4096);
+ } catch (PGPException e) {
+ throw new SmackOpenPgpException("Could not generate OpenPGP Key Pair.", e);
+ }
+
+ return new KeyBytesAndFingerprint(secretKey.getEncoded(), getFingerprint(secretKey.getPublicKey()));
+ }
+
+ @Override
+ public OpenPgpV4Fingerprint importPublicKey(BareJid owner, byte[] bytes) throws MissingUserIdOnKeyException {
+ return null;
+ }
+
+ @Override
+ public OpenPgpV4Fingerprint importSecretKey(BareJid owner, byte[] bytes) throws MissingUserIdOnKeyException {
+ return null;
+ }
+
+ public static OpenPgpV4Fingerprint getFingerprint(PGPPublicKey publicKey) {
+ byte[] hex = Hex.encode(publicKey.getFingerprint());
+ return new OpenPgpV4Fingerprint(hex);
+ }
+}
diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/PainlessOpenPgpStore.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/PainlessOpenPgpStore.java
new file mode 100644
index 000000000..41819a748
--- /dev/null
+++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/PainlessOpenPgpStore.java
@@ -0,0 +1,40 @@
+/**
+ *
+ * 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.bouncycastle;
+
+import java.io.IOException;
+
+import org.jivesoftware.smackx.ox.OpenPgpStore;
+
+import de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
+import org.jxmpp.jid.BareJid;
+
+public interface PainlessOpenPgpStore extends OpenPgpStore {
+
+ PGPPublicKeyRingCollection getPublicKeyRings(BareJid owner) throws IOException, PGPException;
+
+ PGPSecretKeyRingCollection getSecretKeyRing(BareJid owner) throws IOException, PGPException;
+
+ void storePublicKeyRing(BareJid owner, PGPPublicKeyRingCollection publicKeys) throws IOException;
+
+ void storeSecretKeyRing(BareJid owner, PGPSecretKeyRingCollection secretKeys) throws IOException;
+
+ SecretKeyRingProtector getSecretKeyProtector();
+}
diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/selection_strategy/BareJidUserId.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/selection_strategy/BareJidUserId.java
new file mode 100644
index 000000000..d3f763d99
--- /dev/null
+++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/selection_strategy/BareJidUserId.java
@@ -0,0 +1,42 @@
+package org.jivesoftware.smackx.ox.bouncycastle.selection_strategy;
+
+import java.util.Iterator;
+
+import de.vanitasvitae.crypto.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy;
+import de.vanitasvitae.crypto.pgpainless.key.selection.keyring.SecretKeyRingSelectionStrategy;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPSecretKeyRing;
+import org.jxmpp.jid.BareJid;
+
+public class BareJidUserId {
+
+ public static class PubRingSelectionStrategy extends PublicKeyRingSelectionStrategy {
+
+ @Override
+ public boolean accept(BareJid jid, PGPPublicKeyRing ring) {
+ Iterator userIds = ring.getPublicKey().getUserIDs();
+ while (userIds.hasNext()) {
+ String userId = userIds.next();
+ if (userId.equals("xmpp:" + jid.toString())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ public static class SecRingSelectionStrategy extends SecretKeyRingSelectionStrategy {
+
+ @Override
+ public boolean accept(BareJid jid, PGPSecretKeyRing ring) {
+ Iterator userIds = ring.getPublicKey().getUserIDs();
+ while (userIds.hasNext()) {
+ String userId = userIds.next();
+ if (userId.equals("xmpp:" + jid.toString())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpStore.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/selection_strategy/package-info.java
similarity index 64%
rename from smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpStore.java
rename to smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/selection_strategy/package-info.java
index fff699308..e8e78da85 100644
--- a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpStore.java
+++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/selection_strategy/package-info.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2018 Paul Schaub.
+ * Copyright 2017 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jivesoftware.smackx.ox.bouncycastle;
-
-import org.jivesoftware.smackx.ox.OpenPgpStore;
-
-import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfig;
-
-public interface BCOpenPgpStore extends OpenPgpStore {
-
- KeyringConfig getKeyringConfig();
-}
+/**
+ * Providers for XEP-0373: OpenPGP for XMPP using Bouncycastle.
+ */
+package org.jivesoftware.smackx.ox.bouncycastle.selection_strategy;
diff --git a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastleOpenPgpProviderTest.java b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastleOpenPgpProviderTest.java
index 91da7a414..d784c3c16 100644
--- a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastleOpenPgpProviderTest.java
+++ b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastleOpenPgpProviderTest.java
@@ -25,7 +25,7 @@ import java.util.Collections;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.test.util.SmackTestSuite;
-import org.jivesoftware.smackx.ox.OpenPgpMessage;
+import org.jivesoftware.smackx.ox.chat.OpenPgpMessage;
import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
import org.jivesoftware.smackx.ox.element.PubkeyElement;
@@ -54,10 +54,10 @@ public class BouncyCastleOpenPgpProviderTest extends SmackTestSuite {
// dry exchange keys
- PubkeyElement aliceKeys = aliceProvider.createPubkeyElement(aliceProvider.primaryOpenPgpKeyPairFingerprint());
- PubkeyElement cheshireKeys = cheshireProvider.createPubkeyElement(cheshireProvider.primaryOpenPgpKeyPairFingerprint());
- aliceProvider.storePublicKey(cheshire, cheshireProvider.primaryOpenPgpKeyPairFingerprint(), cheshireKeys);
- cheshireProvider.storePublicKey(alice, aliceProvider.primaryOpenPgpKeyPairFingerprint(), aliceKeys);
+ PubkeyElement aliceKeys = aliceProvider.createPubkeyElement(aliceProvider.getPrimaryOpenPgpKeyPairFingerprint());
+ PubkeyElement cheshireKeys = cheshireProvider.createPubkeyElement(cheshireProvider.getPrimaryOpenPgpKeyPairFingerprint());
+ aliceProvider.storePublicKey(cheshire, cheshireProvider.getPrimaryOpenPgpKeyPairFingerprint(), cheshireKeys);
+ cheshireProvider.storePublicKey(alice, aliceProvider.getPrimaryOpenPgpKeyPairFingerprint(), aliceKeys);
// Create signed and encrypted message from alice to the cheshire cat
SigncryptElement signcryptElement = new SigncryptElement(
@@ -66,11 +66,11 @@ public class BouncyCastleOpenPgpProviderTest extends SmackTestSuite {
new Message.Body("en", "How do you know I’m mad?")));
OpenPgpElement encrypted = aliceProvider.signAndEncrypt(
signcryptElement,
- aliceProvider.primaryOpenPgpKeyPairFingerprint(),
- Collections.singleton(cheshireProvider.primaryOpenPgpKeyPairFingerprint()));
+ aliceProvider.getPrimaryOpenPgpKeyPairFingerprint(),
+ Collections.singleton(cheshireProvider.getPrimaryOpenPgpKeyPairFingerprint()));
// Decrypt the message as the cheshire cat
- OpenPgpMessage decrypted = cheshireProvider.decryptAndVerify(encrypted, Collections.singleton(aliceProvider.primaryOpenPgpKeyPairFingerprint()));
+ OpenPgpMessage decrypted = cheshireProvider.decryptAndVerify(encrypted, Collections.singleton(aliceProvider.getPrimaryOpenPgpKeyPairFingerprint()));
OpenPgpContentElement content = decrypted.getOpenPgpContentElement();
assertTrue(content instanceof SigncryptElement);
diff --git a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastle_OpenPgpV4FingerprintTest.java b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastle_OpenPgpV4FingerprintTest.java
index 03ac5c659..1241c3ad9 100644
--- a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastle_OpenPgpV4FingerprintTest.java
+++ b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastle_OpenPgpV4FingerprintTest.java
@@ -24,7 +24,7 @@ import java.io.IOException;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
import org.jivesoftware.smackx.ox.TestKeys;
-import org.jivesoftware.smackx.ox.Util;
+import org.jivesoftware.smackx.ox.util.Util;
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.KeyringConfigCallbacks;
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.InMemoryKeyring;
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 718841a4b..b6079a78b 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
@@ -17,6 +17,7 @@
package org.jivesoftware.smackx.ox;
import java.io.IOException;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -32,14 +33,18 @@ import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.chat2.ChatManager;
import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.ox.chat.OpenPgpEncryptedChat;
+import org.jivesoftware.smackx.ox.chat.OpenPgpFingerprints;
+import org.jivesoftware.smackx.ox.chat.OpenPgpMessage;
import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
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;
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
import org.jivesoftware.smackx.ox.listener.OpenPgpEncryptedMessageListener;
+import org.jivesoftware.smackx.ox.util.DecryptedBytesAndMetadata;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid;
@@ -62,6 +67,7 @@ public final class OXInstantMessagingManager extends Manager {
private final ChatManager chatManager;
private final Set chatMessageListeners = new HashSet<>();
+ private final Map chats = new HashMap<>();
private OXInstantMessagingManager(final XMPPConnection connection) {
super(connection);
@@ -144,23 +150,29 @@ public final class OXInstantMessagingManager extends Manager {
try {
OpenPgpEncryptedChat encryptedChat = chatWith(from);
- OpenPgpMessage decrypted = provider.decryptAndVerify(element, provider.availableOpenPgpPublicKeysFingerprints(from.asBareJid()));
- OpenPgpContentElement contentElement = decrypted.getOpenPgpContentElement();
- if (decrypted.getState() != OpenPgpMessage.State.signcrypt) {
+ DecryptedBytesAndMetadata decryptedBytes = provider.decrypt(Base64.decode(
+ element.getEncryptedBase64MessageContent()),
+ from.asBareJid(),
+ null);
+
+ OpenPgpMessage openPgpMessage = new OpenPgpMessage(decryptedBytes.getBytes(),
+ new OpenPgpMessage.Metadata(decryptedBytes.getDecryptionKey(),
+ decryptedBytes.getVerifiedSignatures()));
+
+ OpenPgpContentElement contentElement = openPgpMessage.getOpenPgpContentElement();
+ if (openPgpMessage.getState() != OpenPgpMessage.State.signcrypt) {
LOGGER.log(Level.WARNING, "Decrypted content is not a signcrypt element. Ignore it.");
return;
}
SigncryptElement signcryptElement = (SigncryptElement) contentElement;
for (OpenPgpEncryptedMessageListener l : chatMessageListeners) {
- l.newIncomingEncryptedMessage(from, message, signcryptElement, encryptedChat);
+ l.newIncomingOxMessage(from, message, signcryptElement, encryptedChat);
}
} catch (SmackOpenPgpException e) {
LOGGER.log(Level.WARNING, "Could not start chat with " + from, e);
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
LOGGER.log(Level.WARNING, "Something went wrong.", e);
- } catch (MissingOpenPgpPublicKeyException e) {
- LOGGER.log(Level.WARNING, "Could not verify message " + message.getStanzaId() + ": Missing senders public key " + e.getFingerprint().toString(), e);
} catch (MissingOpenPgpKeyPairException e) {
LOGGER.log(Level.WARNING, "Could not decrypt message " + message.getStanzaId() + ": Missing secret key", e);
} catch (XmlPullParserException | IOException e) {
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 653b8d8ee..ee2ccf4e8 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
@@ -16,14 +16,18 @@
*/
package org.jivesoftware.smackx.ox;
-import static org.jivesoftware.smackx.ox.PubSubDelegate.PEP_NODE_PUBLIC_KEYS;
-import static org.jivesoftware.smackx.ox.PubSubDelegate.PEP_NODE_PUBLIC_KEYS_NOTIFY;
-import static org.jivesoftware.smackx.ox.PubSubDelegate.fetchPubkey;
-import static org.jivesoftware.smackx.ox.PubSubDelegate.publishPublicKey;
+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.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
+import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -37,18 +41,23 @@ import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.Async;
+import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.ox.callback.AskForBackupCodeCallback;
import org.jivesoftware.smackx.ox.callback.DisplayBackupCodeCallback;
import org.jivesoftware.smackx.ox.callback.SecretKeyBackupSelectionCallback;
import org.jivesoftware.smackx.ox.callback.SecretKeyRestoreSelectionCallback;
+import org.jivesoftware.smackx.ox.chat.OpenPgpFingerprints;
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.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.SmackOpenPgpException;
+import org.jivesoftware.smackx.ox.util.KeyBytesAndFingerprint;
+import org.jivesoftware.smackx.ox.util.PubSubDelegate;
import org.jivesoftware.smackx.pep.PEPListener;
import org.jivesoftware.smackx.pep.PEPManager;
import org.jivesoftware.smackx.pubsub.EventElement;
@@ -132,20 +141,35 @@ public final class OpenPgpManager extends Manager {
* @throws SmackException.NotConnectedException
* @throws SmackException.NoResponseException
*/
- public void announceSupportAndPublish() throws NoSuchAlgorithmException, NoSuchProviderException, SmackOpenPgpException,
+ public void announceSupportAndPublish()
+ throws NoSuchAlgorithmException, NoSuchProviderException, SmackOpenPgpException,
InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
- SmackException.NotConnectedException, SmackException.NoResponseException {
+ SmackException.NotConnectedException, SmackException.NoResponseException, IOException,
+ InvalidAlgorithmParameterException, SmackException.NotLoggedInException {
throwIfNoProviderSet();
+ throwIfNotAuthenticated();
+
+ BareJid ourJid = connection().getUser().asBareJid();
+
+ OpenPgpV4Fingerprint primaryFingerprint = getOurFingerprint();
- OpenPgpV4Fingerprint primaryFingerprint = provider.primaryOpenPgpKeyPairFingerprint();
if (primaryFingerprint == null) {
- primaryFingerprint = provider.createOpenPgpKeyPair();
+
+ KeyBytesAndFingerprint bytesAndFingerprint = provider.generateOpenPgpKeyPair(ourJid);
+ primaryFingerprint = bytesAndFingerprint.getFingerprint();
+
+ // This should never throw, since we set our jid literally one line above this comment.
+ try {
+ provider.importSecretKey(ourJid, bytesAndFingerprint.getBytes());
+ } catch (MissingUserIdOnKeyException e) {
+ throw new AssertionError(e);
+ }
}
// Create element
PubkeyElement pubkeyElement;
try {
- pubkeyElement = provider.createPubkeyElement(primaryFingerprint);
+ pubkeyElement = createPubkeyElement(ourJid, primaryFingerprint, new Date());
} catch (MissingOpenPgpPublicKeyException e) {
throw new AssertionError("Cannot publish our public key, since it is missing (MUST NOT happen!)");
}
@@ -166,7 +190,7 @@ public final class OpenPgpManager extends Manager {
*/
public OpenPgpV4Fingerprint getOurFingerprint() {
throwIfNoProviderSet();
- return provider.primaryOpenPgpKeyPairFingerprint();
+ return provider.getStore().getPrimaryOpenPgpKeyPairFingerprint();
}
/**
@@ -183,7 +207,8 @@ public final class OpenPgpManager extends Manager {
*/
public boolean serverSupportsSecretKeyBackups()
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
- SmackException.NoResponseException {
+ SmackException.NoResponseException, SmackException.NotLoggedInException {
+ throwIfNotAuthenticated();
boolean pep = PEPManager.getInstanceFor(connection()).isSupported();
boolean whitelist = PubSubManager.getInstance(connection(), connection().getUser().asBareJid())
.getSupportedFeatures().containsFeature("http://jabber.org/protocol/pubsub#access-whitelist");
@@ -197,7 +222,6 @@ public final class OpenPgpManager extends Manager {
*
* @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 SmackOpenPgpException if the secret key is corrupted or can for some reason not be serialized.
* @throws InterruptedException
* @throws PubSubException.NotALeafNodeException
* @throws XMPPException.XMPPErrorException
@@ -206,14 +230,20 @@ public final class OpenPgpManager extends Manager {
*/
public void backupSecretKeyToServer(DisplayBackupCodeCallback displayCodeCallback,
SecretKeyBackupSelectionCallback selectKeyCallback)
- throws SmackOpenPgpException, InterruptedException, PubSubException.NotALeafNodeException,
+ throws InterruptedException, PubSubException.NotALeafNodeException,
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException,
- MissingOpenPgpKeyPairException {
+ SmackException.NotLoggedInException {
throwIfNoProviderSet();
+ throwIfNotAuthenticated();
+
+ BareJid ownJid = connection().getUser().asBareJid();
+
String backupCode = generateBackupPassword();
- Set availableKeyPairs = provider.availableOpenPgpKeyPairFingerprints();
- SecretkeyElement secretKey = provider.createSecretkeyElement(
- selectKeyCallback.selectKeysToBackup(availableKeyPairs), backupCode);
+ Set availableKeyPairs = provider.getStore().getAvailableKeyPairFingerprints();
+ Set selectedKeyPairs = selectKeyCallback.selectKeysToBackup(availableKeyPairs);
+
+ SecretkeyElement secretKey = createSecretkeyElement(ownJid, selectedKeyPairs, backupCode);
+
PubSubDelegate.depositSecretKey(connection(), secretKey);
displayCodeCallback.displayBackupCode(backupCode);
}
@@ -228,7 +258,8 @@ public final class OpenPgpManager extends Manager {
*/
public void deleteSecretKeyServerBackup()
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
- SmackException.NoResponseException {
+ SmackException.NoResponseException, SmackException.NotLoggedInException {
+ throwIfNotAuthenticated();
PubSubDelegate.deleteSecretKeyNode(connection());
}
@@ -249,10 +280,16 @@ public final class OpenPgpManager extends Manager {
SecretKeyRestoreSelectionCallback selectionCallback)
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, SmackException.NoResponseException, SmackOpenPgpException,
- InvalidBackupCodeException {
+ InvalidBackupCodeException, SmackException.NotLoggedInException {
throwIfNoProviderSet();
+ throwIfNotAuthenticated();
SecretkeyElement backup = PubSubDelegate.fetchSecretKey(connection());
- provider.restoreSecretKeyBackup(backup, codeCallback.askForBackupCode(), selectionCallback);
+ if (backup == null) {
+ // TODO
+ return;
+ }
+ byte[] encrypted = Base64.decode(backup.getB64Data());
+ // provider.restoreSecretKeyBackup(backup, codeCallback.askForBackupCode(), selectionCallback);
// TODO: catch InvalidBackupCodeException in order to prevent re-fetching the backup on next try.
}
@@ -270,22 +307,28 @@ public final class OpenPgpManager extends Manager {
public OpenPgpFingerprints determineContactsKeys(BareJid jid)
throws SmackOpenPgpException, InterruptedException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, SmackException.NoResponseException {
- Set announced = provider.announcedOpenPgpKeyFingerprints(jid);
- Set available = provider.availableOpenPgpPublicKeysFingerprints(jid);
+ 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);
- provider.storePublicKey(jid, f, pubkeyElement);
+ if (pubkeyElement == null) {
+ continue;
+ }
+ processPublicKey(pubkeyElement, jid);
available.add(f);
} catch (PubSubException.NotAPubSubNodeException | PubSubException.NotALeafNodeException 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);
}
}
}
- return new OpenPgpFingerprints(announced, available, unfetched);
+ return new OpenPgpFingerprints(jid, announced, available, unfetched);
}
/**
@@ -295,7 +338,7 @@ public final class OpenPgpManager extends Manager {
*/
private final PEPListener metadataListener = new PEPListener() {
@Override
- public void eventReceived(final EntityBareJid from, final EventElement event, Message message) {
+ public void eventReceived(final EntityBareJid from, final EventElement event, final Message message) {
if (PEP_NODE_PUBLIC_KEYS.equals(event.getEvent().getNode())) {
final BareJid contact = from.asBareJid();
LOGGER.log(Level.INFO, "Received OpenPGP metadata update from " + contact);
@@ -305,17 +348,22 @@ public final class OpenPgpManager extends Manager {
ItemsExtension items = (ItemsExtension) event.getExtensions().get(0);
PayloadItem> payload = (PayloadItem) items.getItems().get(0);
PublicKeysListElement listElement = (PublicKeysListElement) payload.getPayload();
- provider.storePublicKeysList(connection(), listElement, contact);
+
+ 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 missingKeys = listElement.getMetadata().keySet();
-
try {
- provider.storePublicKeysList(connection(), listElement, contact);
- missingKeys.removeAll(provider.availableOpenPgpPublicKeysFingerprints(contact));
+ missingKeys.removeAll(provider.getStore().getAvailableKeysFingerprints(contact).keySet());
for (OpenPgpV4Fingerprint missing : missingKeys) {
try {
PubkeyElement pubkeyElement = fetchPubkey(connection(), contact, missing);
- provider.storePublicKey(contact, missing, pubkeyElement);
+ processPublicKey(pubkeyElement, contact);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Error fetching missing OpenPGP key " + missing.toString(), e);
}
@@ -329,6 +377,16 @@ public final class OpenPgpManager extends Manager {
}
};
+ /*
+ Private stuff.
+ */
+
+ private void processPublicKey(PubkeyElement pubkeyElement, BareJid owner)
+ throws MissingUserIdOnKeyException {
+ byte[] base64 = pubkeyElement.getDataElement().getB64Data();
+ provider.importPublicKey(owner, Base64.decode(base64));
+ }
+
/**
* Generate a secure backup code.
*
@@ -357,6 +415,39 @@ public final class OpenPgpManager extends Manager {
return code.toString();
}
+ private PubkeyElement createPubkeyElement(BareJid owner,
+ OpenPgpV4Fingerprint fingerprint,
+ Date date)
+ throws MissingOpenPgpPublicKeyException {
+ byte[] keyBytes = provider.getStore().getPublicKeyBytes(owner, fingerprint);
+ return createPubkeyElement(keyBytes, date);
+ }
+
+ private static PubkeyElement createPubkeyElement(byte[] bytes, Date date) {
+ return new PubkeyElement(new PubkeyElement.PubkeyDataElement(Base64.encode(bytes)), date);
+ }
+
+ private SecretkeyElement createSecretkeyElement(BareJid owner,
+ Set fingerprints,
+ String backupCode) {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ for (OpenPgpV4Fingerprint fingerprint : fingerprints) {
+ try {
+ byte[] bytes = provider.getStore().getSecretKeyBytes(owner, fingerprint);
+ buffer.write(bytes);
+ } catch (MissingOpenPgpKeyPairException | IOException e) {
+ LOGGER.log(Level.WARNING, "Cannot backup secret key " + Long.toHexString(fingerprint.getKeyId()) + ".", e);
+ }
+
+ }
+ return createSecretkeyElement(buffer.toByteArray(), backupCode);
+ }
+
+ private SecretkeyElement createSecretkeyElement(byte[] keys, String backupCode) {
+ byte[] encrypted = provider.symmetricallyEncryptWithPassword(keys, backupCode);
+ return new SecretkeyElement(Base64.encode(encrypted));
+ }
+
/**
* Throw an {@link IllegalStateException} if no {@link OpenPgpProvider} is set.
* The OpenPgpProvider is used to process information related to RFC-4880.
@@ -366,4 +457,10 @@ public final class OpenPgpManager extends Manager {
throw new IllegalStateException("No OpenPgpProvider set!");
}
}
+
+ private void throwIfNotAuthenticated() throws SmackException.NotLoggedInException {
+ if (!connection().isAuthenticated()) {
+ throw new SmackException.NotLoggedInException();
+ }
+ }
}
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 f3c03d384..a9f1a194e 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
@@ -16,122 +16,126 @@
*/
package org.jivesoftware.smackx.ox;
-import java.util.Set;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import org.jivesoftware.smack.util.MultiMap;
+import org.jivesoftware.smackx.ox.callback.SmackMissingOpenPgpPublicKeyCallback;
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.SignElement;
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.util.DecryptedBytesAndMetadata;
+import org.jivesoftware.smackx.ox.util.KeyBytesAndFingerprint;
-public interface OpenPgpProvider extends OpenPgpStore {
+import org.jxmpp.jid.BareJid;
+
+public interface OpenPgpProvider {
/**
* Sign and encrypt a {@link SigncryptElement} element for usage within the context of instant messaging.
- * The resulting {@link OpenPgpElement} contains a Base64 encoded, unarmored OpenPGP message,
- * which can be decrypted by each recipient, as well as by ourselves.
+ * The resulting byte array can be decrypted by each recipient, as well as all devices of the user.
* The message contains a signature made by our key.
*
* @see XEP-0373 §3
* @see XEP-0374 §2.1
+ *
* @param element {@link SigncryptElement} which contains the content of the message as plaintext.
* @param signingKey {@link OpenPgpV4Fingerprint} of the signing key.
- * @param encryptionKeys {@link Set} containing all {@link OpenPgpV4Fingerprint}s of keys which will
+ * @param encryptionKeys {@link MultiMap} containing all {@link OpenPgpV4Fingerprint}s of recipients which will
* be able to decrypt the message.
- * @return encrypted {@link OpenPgpElement} which contains the encrypted, encoded message.
+ * @return encrypted and signed data which contains the encrypted, encoded message.
+ *
* @throws MissingOpenPgpKeyPairException if the OpenPGP key pair with the given {@link OpenPgpV4Fingerprint}
* is not available.
* @throws MissingOpenPgpKeyPairException if any of the OpenPGP public keys whose {@link OpenPgpV4Fingerprint}
* is listed in {@code encryptionKeys} is not available.
*/
- OpenPgpElement signAndEncrypt(SigncryptElement element,
- OpenPgpV4Fingerprint signingKey,
- Set encryptionKeys)
- throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException;
+ byte[] signAndEncrypt(SigncryptElement element,
+ OpenPgpV4Fingerprint signingKey,
+ MultiMap encryptionKeys)
+ throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException, SmackOpenPgpException, IOException;
/**
- * Decrypt an incoming {@link OpenPgpElement} which must contain a {@link SigncryptElement} and verify
- * the signature made by the sender in the context of instant messaging.
- *
- * @see XEP-0374 §2.1
- * @param element {@link OpenPgpElement} which contains an encrypted and signed {@link SigncryptElement}.
- * @param sendersKeys {@link Set} of the senders {@link OpenPgpV4Fingerprint}s.
- * It is required, that one of those keys was used for signing the message.
- * @return decrypted {@link OpenPgpMessage} which contains the decrypted {@link SigncryptElement}.
- * @throws MissingOpenPgpKeyPairException if we have no OpenPGP key pair to decrypt the message.
- * @throws MissingOpenPgpPublicKeyException if we do not have the public OpenPGP key of the sender to
- * verify the signature on the message.
- */
- OpenPgpMessage decryptAndVerify(OpenPgpElement element, Set sendersKeys)
- throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException;
-
- /**
- * Sign a {@link SignElement} and pack it inside a {@link OpenPgpElement}.
- * The resulting {@link OpenPgpElement} contains the {@link SignElement} signed and base64 encoded.
- *
- * Note: DO NOT use this method in the context of instant messaging, as XEP-0374 forbids that.
+ * Sign a {@link SignElement} with th users signing key.
+ * The resulting byte array contains the signed byte representation of the {@link SignElement}.
*
* @see XEP-0373 §3.1
* @see XEP-0374 §2.1
+ *
* @param element {@link SignElement} which will be signed.
* @param singingKeyFingerprint {@link OpenPgpV4Fingerprint} of the key that is used for signing.
- * @return {@link OpenPgpElement} which contains the signed, Base64 encoded {@link SignElement}.
+ * @return byte array which contains the signed {@link SignElement}.
+ *
* @throws MissingOpenPgpKeyPairException if we don't have the key pair for the
* {@link OpenPgpV4Fingerprint} available.
*/
- OpenPgpElement sign(SignElement element, OpenPgpV4Fingerprint singingKeyFingerprint)
- throws MissingOpenPgpKeyPairException;
+ byte[] sign(SignElement element, OpenPgpV4Fingerprint singingKeyFingerprint)
+ throws MissingOpenPgpKeyPairException, IOException, SmackOpenPgpException;
/**
- * Verify the signature on an incoming {@link OpenPgpElement} which must contain a {@link SignElement}.
- *
- * Note: DO NOT use this method in the context of instant messaging, as XEP-0374 forbids that.
- *
- * @see XEP-0373 §3.1
- * @see XEP-0374 §2.1
- * @param element incoming {@link OpenPgpElement} which must contain a signed {@link SignElement}.
- * @param singingKeyFingerprints {@link Set} of the senders key {@link OpenPgpV4Fingerprint}s.
- * It is required that one of those keys was used to sign
- * the message.
- * @return {@link OpenPgpMessage} which contains the decoded {@link SignElement}.
- * @throws MissingOpenPgpPublicKeyException if we don't have the signers public key which signed
- * the message available.
- */
- OpenPgpMessage verify(OpenPgpElement element, Set singingKeyFingerprints)
- throws MissingOpenPgpPublicKeyException;
-
- /**
- * Encrypt a {@link CryptElement} and pack it inside a {@link OpenPgpElement}.
- * The resulting {@link OpenPgpElement} contains the encrypted and Base64 encoded {@link CryptElement}
+ * Encrypt a {@link CryptElement} for all keys which fingerprints are contained in
+ * {@code encryptionKeyFingerprints}.
+ * The resulting byte array contains the encrypted {@link CryptElement}
* which can be decrypted by all recipients, as well as by ourselves.
*
* Note: DO NOT use this method in the context of instant messaging, as XEP-0374 forbids that.
*
* @see XEP-0374 §2.1
+ *
* @param element plaintext {@link CryptElement} which will be encrypted.
- * @param encryptionKeyFingerprints {@link Set} of {@link OpenPgpV4Fingerprint}s of the keys which
- * are used for encryption.
- * @return {@link OpenPgpElement} which contains the encrypted, Base64 encoded {@link CryptElement}.
+ * @param encryptionKeyFingerprints {@link MultiMap} of recipients and {@link OpenPgpV4Fingerprint}s of the
+ * keys which are used for encryption.
+ * @return byte array which contains the encrypted {@link CryptElement}.
* @throws MissingOpenPgpPublicKeyException if any of the OpenPGP public keys whose
* {@link OpenPgpV4Fingerprint} is listed in {@code encryptionKeys}
* is not available.
*/
- OpenPgpElement encrypt(CryptElement element, Set encryptionKeyFingerprints)
- throws MissingOpenPgpPublicKeyException;
+ byte[] encrypt(CryptElement element, MultiMap encryptionKeyFingerprints)
+ throws MissingOpenPgpPublicKeyException, IOException, SmackOpenPgpException;
/**
- * Decrypt an incoming {@link OpenPgpElement} which must contain a {@link CryptElement}.
- * The resulting {@link OpenPgpMessage} will contain the decrypted {@link CryptElement}.
- *
- * Note: DO NOT use this method in the context of instant messaging, as XEP-0374 forbids that.
+ * Process an incoming {@link OpenPgpElement}.
+ * If its content is encrypted ({@link CryptElement} or {@link SigncryptElement}), the content will be decrypted.
+ * If its content is signed ({@link SignElement} or {@link SigncryptElement}), signatures are verified using
+ * the announced public keys of the sender.
+ * The resulting byte array will contain the decrypted {@link OpenPgpContentElement}.
+ *
+ * @see XEP-0373 §3.1
+ *
+ * @param bytes byte array which contains the encrypted {@link OpenPgpContentElement}.
+ * @return byte array which contains the decrypted {@link OpenPgpContentElement}, as well as metadata.
*
- * @see XEP-0374 §2.1
- * @param element {@link OpenPgpElement} which contains the encrypted {@link CryptElement}.
- * @return {@link OpenPgpMessage} which contains the decrypted {@link CryptElement}.
* @throws MissingOpenPgpKeyPairException if we don't have an OpenPGP key pair available that to decrypt
* the message.
*/
- OpenPgpMessage decrypt(OpenPgpElement element)
- throws MissingOpenPgpKeyPairException;
+ DecryptedBytesAndMetadata decrypt(byte[] bytes, BareJid sender, SmackMissingOpenPgpPublicKeyCallback missingPublicKeyCallback)
+ throws MissingOpenPgpKeyPairException, SmackOpenPgpException, IOException;
+
+ byte[] symmetricallyEncryptWithPassword(byte[] bytes, String password) throws SmackOpenPgpException, IOException;
+
+ byte[] symmetricallyDecryptWithPassword(byte[] bytes, String password) throws SmackOpenPgpException, IOException;
+
+ /**
+ * Generate a fresh OpenPGP key pair.
+ *
+ * @param owner JID of the keys owner.
+ * @return byte array representation + {@link OpenPgpV4Fingerprint} of the generated key pair.
+ */
+ KeyBytesAndFingerprint generateOpenPgpKeyPair(BareJid owner)
+ throws SmackOpenPgpException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
+ NoSuchProviderException, IOException;
+
+ OpenPgpV4Fingerprint importPublicKey(BareJid owner, byte[] bytes) throws MissingUserIdOnKeyException;
+
+ OpenPgpV4Fingerprint importSecretKey(BareJid owner, byte[] bytes) throws MissingUserIdOnKeyException;
+
+ OpenPgpStore getStore();
}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java
index 17485f9d0..79f1281fa 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java
@@ -16,19 +16,11 @@
*/
package org.jivesoftware.smackx.ox;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
import java.util.Date;
import java.util.Map;
import java.util.Set;
-import org.jivesoftware.smack.XMPPConnection;
-import org.jivesoftware.smackx.ox.callback.SecretKeyRestoreSelectionCallback;
-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.SigncryptElement;
-import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
+import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
@@ -45,123 +37,105 @@ public interface OpenPgpStore {
*
* @return fingerprint of the primary OpenPGP key pair.
*/
- OpenPgpV4Fingerprint primaryOpenPgpKeyPairFingerprint();
+ OpenPgpV4Fingerprint getPrimaryOpenPgpKeyPairFingerprint();
/**
- * Return a {@link Set} containing the {@link OpenPgpV4Fingerprint} of all available OpenPGP key pairs.
+ * Set the {@link OpenPgpV4Fingerprint} of the primary OpenPGP key pair.
+ * If multiple key pairs are available, only the primary key pair is used for signing.
*
- * @return set of fingerprints of available OpenPGP key pairs.
+ * @param fingerprint {@link OpenPgpV4Fingerprint} of the new primary key pair.
*/
- Set availableOpenPgpKeyPairFingerprints();
+ void setPrimaryOpenPgpKeyPairFingerprint(OpenPgpV4Fingerprint fingerprint);
+
+ /**
+ * Return a {@link Set} containing the {@link OpenPgpV4Fingerprint}s of the master keys of all available
+ * OpenPGP key pairs.
+ *
+ * @return set of fingerprints of available OpenPGP key pairs master keys.
+ */
+ Set getAvailableKeyPairFingerprints();
+
+ /**
+ * Return a {@link Map} containing the {@link OpenPgpV4Fingerprint}s of all OpenPGP public keys of a
+ * contact, which we have locally available, as well as the date, those keys had been published on.
+ *
+ * Note: This returns a {@link Map} that might be different from the result of
+ * {@link #getAvailableKeysFingerprints(BareJid)} (BareJid)}.
+ * Messages should be encrypted to the intersection of both key sets.
+ *
+ * @param contact contact.
+ * @return list of contacts locally available public keys.
+ *
+ * @throws SmackOpenPgpException if something goes wrong
+ */
+ Map getAvailableKeysFingerprints(BareJid contact)
+ throws SmackOpenPgpException;
/**
* Return a {@link Map} containing the {@link OpenPgpV4Fingerprint}s of all currently announced OpenPGP
- * public keys of a contact along with the dates of their latest update.
+ * public keys of a contact along with the dates of their latest revision.
*
* Note: Those are the keys announced in the latest received metadata update.
* This returns a {@link Map} which might contain different {@link OpenPgpV4Fingerprint}s than the result of
- * {@link #availableOpenPgpPublicKeysFingerprints(BareJid)}.
- * Messages should be encrypted to the intersection of both sets.
+ * {@link #getAvailableKeysFingerprints(BareJid)} (BareJid)}.
+ * Messages should be encrypted to the intersection of both key sets.
*
* @param contact contact.
* @return map of contacts last announced public keys and their update dates.
*/
- Map announcedOpenPgpKeyFingerprints(BareJid contact);
+ Map getAnnouncedKeysFingerprints(BareJid contact);
/**
- * Return a {@link Set} containing the {@link OpenPgpV4Fingerprint}s of all OpenPGP public keys of a
- * contact, which we have locally available.
- *
- * Note: This returns a {@link Set} that might be different from the result of
- * {@link #availableOpenPgpPublicKeysFingerprints(BareJid)}.
- * Messages should be encrypted to the intersection of both sets.
+ * Store a {@Map} of a contacts fingerprints and publication dates in persistent storage.
*
- * @param contact contact.
- * @return list of contacts locally available public keys.
- * @throws SmackOpenPgpException if something goes wrong
+ * @param contact {@link BareJid} of the owner of the announced public keys.
+ * @param fingerprints {@link Map} which contains a list of the keys of {@code owner}.
*/
- Set availableOpenPgpPublicKeysFingerprints(BareJid contact)
- throws SmackOpenPgpException;
+ void setAnnouncedKeysFingerprints(BareJid contact, Map fingerprints);
/**
- * Store incoming update to the OpenPGP metadata node in persistent storage.
- *
- * @param connection authenticated {@link XMPPConnection} of the user.
- * @param listElement {@link PublicKeysListElement} which contains a list of the keys of {@code owner}.
- * @param owner {@link BareJid} of the owner of the announced public keys.
- */
- void storePublicKeysList(XMPPConnection connection, PublicKeysListElement listElement, BareJid owner);
-
- /**
- * Create a fresh OpenPGP key pair with the {@link BareJid} of the user prefixed by "xmpp:" as user-id
- * (example: {@code "xmpp:juliet@capulet.lit"}).
- * Store the key pair in persistent storage and return the public keys {@link OpenPgpV4Fingerprint}.
- *
- * @return {@link OpenPgpV4Fingerprint} of the generated key pair.
- * @throws NoSuchAlgorithmException if a Hash algorithm is not available
- * @throws NoSuchProviderException id no suitable cryptographic provider (for example BouncyCastleProvider)
- * is registered.
- * @throws SmackOpenPgpException if the generated key cannot be added to the keyring for some reason.
- */
- OpenPgpV4Fingerprint createOpenPgpKeyPair()
- throws NoSuchAlgorithmException, NoSuchProviderException, SmackOpenPgpException;
-
- /**
- * Create a {@link PubkeyElement} which contains our exported OpenPGP public key.
- * The element can for example be published.
- *
- * @return {@link PubkeyElement} containing our public key.
- * @throws MissingOpenPgpPublicKeyException if we have no OpenPGP key pair.
- * @throws SmackOpenPgpException if something goes wrong.
- */
- PubkeyElement createPubkeyElement(OpenPgpV4Fingerprint fingerprint)
- throws SmackOpenPgpException, MissingOpenPgpPublicKeyException;
-
- /**
- * Process an incoming {@link PubkeyElement} of a contact or ourselves.
- * That typically includes importing/updating the key.
- *
- * @param owner owner of the OpenPGP public key contained in the {@link PubkeyElement}.
- * @param fingerprint {@link OpenPgpV4Fingerprint} of the key.
- * @param element {@link PubkeyElement} which presumably contains the public key of the {@code owner}.
- * @param currentMetadataDate {@link Date} which is currently found in the metadata node for this key.
- * @throws SmackOpenPgpException if the key found in the {@link PubkeyElement}
- * can not be deserialized or imported.
- */
- void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element, Date currentMetadataDate)
- throws SmackOpenPgpException;
-
- /**
- * Return the {@link Date} of the last time on which the key has been fetched from PubSub.
+ * Return the {@link Date} of the last revision which was fetched from PubSub.
*
* @param owner owner of the key
* @param fingerprint fingerprint of the key.
* @return {@link Date} or {@code null} if no record found.
*/
- Date getPubkeysLatestUpdateDate(BareJid owner, OpenPgpV4Fingerprint fingerprint);
+ Date getPubkeysLastRevision(BareJid owner, OpenPgpV4Fingerprint fingerprint);
/**
- * Create an encrypted backup of our secret keys.
+ * Set the {@link Date} of the last revision which was fetched from PubSub.
*
- * @param fingerprints {@link Set} of IDs of the keys that will be included in the backup.
- * @param password password that is used to symmetrically encrypt the backup.
- * @return {@link SigncryptElement} containing the selected encrypted secret keys.
- * @throws MissingOpenPgpKeyPairException if we don't have an OpenPGP key available.
- * @throws SmackOpenPgpException if for some reason the key pair cannot be serialized.
+ * @param owner owner of the key
+ * @param fingerprint fingerprint of the key
+ * @param revision {@link Date} of the revision
*/
- SecretkeyElement createSecretkeyElement(Set fingerprints, String password)
- throws MissingOpenPgpKeyPairException, SmackOpenPgpException;
+ void setPubkeysLastRevision(BareJid owner, OpenPgpV4Fingerprint fingerprint, Date revision);
/**
- * Decrypt a secret key backup and restore the key from it.
+ * Return a {@link MultiMap} which contains contacts and their trusted keys {@link OpenPgpV4Fingerprint}s.
*
- * @param secretkeyElement {@link SecretkeyElement} containing the backup.
- * @param password password to decrypt the backup.
- * @param callback {@link SecretKeyRestoreSelectionCallback} to let the user decide which key to restore.
- * @throws SmackOpenPgpException if the selected key is corrupted and cannot be restored or our key ring
- * is corrupted.
- * @throws InvalidBackupCodeException if the user provided backup code is invalid.
+ * @return trusted fingerprints.
*/
- void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback)
- throws SmackOpenPgpException, InvalidBackupCodeException;
+ MultiMap getAllContactsTrustedFingerprints();
+
+ /**
+ * Return the byte array representation of {@code owner}s public key ring with fingerprint {@code fingerprint}.
+ *
+ * @param owner owner of the key
+ * @param fingerprint fingerprint of the key
+ * @return byte representation of the public key.
+ */
+ byte[] getPublicKeyBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
+ throws MissingOpenPgpPublicKeyException;
+
+ /**
+ * Return the byte array representation of {@code owner}s secret key ring with fingerprint {@code fingerprint}.
+ *
+ * @param owner owner of the key
+ * @param fingerprint fingerprint of the key
+ * @return byte representation of the secret key.
+ */
+ byte[] getSecretKeyBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
+ throws MissingOpenPgpKeyPairException;
+
}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpV4Fingerprint.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpV4Fingerprint.java
index da21e4b05..9172f40e8 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpV4Fingerprint.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpV4Fingerprint.java
@@ -19,6 +19,7 @@ package org.jivesoftware.smackx.ox;
import java.nio.charset.Charset;
import org.jivesoftware.smack.util.Objects;
+import org.jivesoftware.smackx.ox.util.Util;
/**
* This class represents an hex encoded, uppercase OpenPGP v4 fingerprint.
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/SmackMissingOpenPgpPublicKeyCallback.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/SmackMissingOpenPgpPublicKeyCallback.java
new file mode 100644
index 000000000..028add7f4
--- /dev/null
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/SmackMissingOpenPgpPublicKeyCallback.java
@@ -0,0 +1,4 @@
+package org.jivesoftware.smackx.ox.callback;
+
+public interface SmackMissingOpenPgpPublicKeyCallback {
+}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpEncryptedChat.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpEncryptedChat.java
similarity index 68%
rename from smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpEncryptedChat.java
rename to smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpEncryptedChat.java
index 956b02fef..1711c635e 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpEncryptedChat.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpEncryptedChat.java
@@ -14,25 +14,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jivesoftware.smackx.ox;
+package org.jivesoftware.smackx.ox.chat;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
+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.OpenPgpProvider;
+import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
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;
+import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
+import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
public class OpenPgpEncryptedChat {
@@ -49,31 +54,35 @@ public class OpenPgpEncryptedChat {
OpenPgpFingerprints contactsFingerprints) {
this.cryptoProvider = cryptoProvider;
this.chat = chat;
- this.singingKey = cryptoProvider.primaryOpenPgpKeyPairFingerprint();
+ this.singingKey = cryptoProvider.getStore().getPrimaryOpenPgpKeyPairFingerprint();
this.ourFingerprints = ourFingerprints;
this.contactsFingerprints = contactsFingerprints;
}
public void send(Message message, List payload)
- throws MissingOpenPgpKeyPairException, SmackException.NotConnectedException, InterruptedException {
- Set encryptionFingerprints = new HashSet<>(contactsFingerprints.getActiveKeys());
- encryptionFingerprints.addAll(ourFingerprints.getActiveKeys());
+ throws MissingOpenPgpKeyPairException, SmackException.NotConnectedException, InterruptedException,
+ SmackOpenPgpException, IOException {
+ MultiMap fingerprints = oursAndRecipientFingerprints();
SigncryptElement preparedPayload = new SigncryptElement(
Collections.singleton(chat.getXmppAddressOfChatPartner()),
payload);
+
OpenPgpElement encryptedPayload;
+ byte[] encryptedMessage;
// Encrypt the payload
try {
- encryptedPayload = cryptoProvider.signAndEncrypt(
+ encryptedMessage = cryptoProvider.signAndEncrypt(
preparedPayload,
singingKey,
- encryptionFingerprints);
+ fingerprints);
} catch (MissingOpenPgpPublicKeyException e) {
throw new AssertionError("Missing OpenPGP public key, even though this should not happen here.", e);
}
+ encryptedPayload = new OpenPgpElement(Base64.encodeToString(encryptedMessage));
+
// Add encrypted payload to message
message.addExtension(encryptedPayload);
@@ -87,9 +96,25 @@ public class OpenPgpEncryptedChat {
}
public void send(Message message, CharSequence body)
- throws MissingOpenPgpKeyPairException, SmackException.NotConnectedException, InterruptedException {
+ throws MissingOpenPgpKeyPairException, SmackException.NotConnectedException, InterruptedException,
+ SmackOpenPgpException, IOException {
List payload = new ArrayList<>();
payload.add(new Message.Body(null, body.toString()));
send(message, payload);
}
+
+ 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;
+ }
}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpFingerprints.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpFingerprints.java
similarity index 90%
rename from smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpFingerprints.java
rename to smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpFingerprints.java
index 9ab70a735..ab67c8bef 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpFingerprints.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpFingerprints.java
@@ -14,13 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jivesoftware.smackx.ox;
+package org.jivesoftware.smackx.ox.chat;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
+
import org.jxmpp.jid.BareJid;
/**
@@ -28,6 +30,7 @@ import org.jxmpp.jid.BareJid;
*/
public class OpenPgpFingerprints {
+ private final BareJid jid;
private final Set announcedKeys;
private final Set availableKeys;
private final Map unfetchableKeys;
@@ -39,14 +42,24 @@ public class OpenPgpFingerprints {
* @param availableKeys keys which contain the contacts {@link BareJid} as user ID.
* @param unfetchableKeys keys that are announced, but cannot be fetched fro PubSub.
*/
- public OpenPgpFingerprints(Set announcedKeys,
+ public OpenPgpFingerprints(BareJid jid,
+ Set announcedKeys,
Set availableKeys,
Map unfetchableKeys) {
+ this.jid = jid;
this.announcedKeys = Collections.unmodifiableSet(announcedKeys);
this.availableKeys = Collections.unmodifiableSet(availableKeys);
this.unfetchableKeys = Collections.unmodifiableMap(unfetchableKeys);
}
+ /**
+ * Return the {@link BareJid} of the user.
+ * @return jid
+ */
+ public BareJid getJid() {
+ return jid;
+ }
+
/**
* Return a {@link Set} of {@link OpenPgpV4Fingerprint}s, which the contact in question announced via their
* metadata node.
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpMessage.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpMessage.java
similarity index 61%
rename from smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpMessage.java
rename to smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpMessage.java
index 028af6885..ef46fd559 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpMessage.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/chat/OpenPgpMessage.java
@@ -14,10 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jivesoftware.smackx.ox;
+package org.jivesoftware.smackx.ox.chat;
import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.HashSet;
+import java.util.Set;
+import org.jivesoftware.smack.util.Objects;
+import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smackx.ox.element.CryptElement;
import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
@@ -49,7 +54,7 @@ public class OpenPgpMessage {
}
private final String element;
- private State state;
+ private final State state;
private OpenPgpContentElement openPgpContentElement;
@@ -60,8 +65,13 @@ public class OpenPgpMessage {
* @param content XML representation of the decrypted {@link OpenPgpContentElement}.
*/
public OpenPgpMessage(State state, String content) {
- this.state = state;
- this.element = content;
+ this.state = Objects.requireNonNull(state);
+ this.element = Objects.requireNonNull(content);
+ }
+
+ public OpenPgpMessage(byte[] bytes, Metadata metadata) {
+ this.element = new String(Base64.decode(bytes), Charset.forName("UTF-8"));
+ this.state = metadata.getState();
}
/**
@@ -90,14 +100,17 @@ public class OpenPgpMessage {
// Determine the state of the content element.
if (openPgpContentElement instanceof SigncryptElement) {
- state = State.signcrypt;
+ if (state != State.signcrypt) {
+ throw new IllegalStateException("OpenPgpContentElement was signed and encrypted, but is not a SigncryptElement.");
+ }
} else if (openPgpContentElement instanceof SignElement) {
- state = State.sign;
+ if (state != State.sign) {
+ throw new IllegalStateException("OpenPgpContentElement was signed and unencrypted, but is not a SignElement.");
+ }
} else if (openPgpContentElement instanceof CryptElement) {
- state = State.crypt;
- } else {
- throw new AssertionError("OpenPgpContentElement is neither a SignElement, " +
- "CryptElement nor a SignCryptElement.");
+ if (state != State.crypt) {
+ throw new IllegalStateException("OpenPgpContentElement was unsigned and encrypted, but is not a CryptElement.");
+ }
}
}
@@ -113,4 +126,40 @@ public class OpenPgpMessage {
ensureOpenPgpContentElementSet();
return state;
}
+
+ public static class Metadata {
+
+ private final Long encryptionKeyId;
+ private final Set validSignatureIds;
+
+ public Metadata(Long encryptionKeyId, Set validSignatureIds) {
+ this.encryptionKeyId = encryptionKeyId;
+ this.validSignatureIds = validSignatureIds;
+ }
+
+ public Long getEncryptionKeyId() {
+ return encryptionKeyId;
+ }
+
+ public Set getValidSignatureIds() {
+ return new HashSet<>(validSignatureIds);
+ }
+
+ public State getState() {
+ if (validSignatureIds.size() != 0) {
+ if (encryptionKeyId != null) {
+ return State.signcrypt;
+ } else {
+ return State.sign;
+ }
+ } else {
+ if (encryptionKeyId != null) {
+ return State.crypt;
+ } else {
+ throw new IllegalStateException("OpenPGP message appears to be neither encrypted, " +
+ "nor signed.");
+ }
+ }
+ }
+ }
}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/MissingOpenPgpKeyPairException.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/MissingOpenPgpKeyPairException.java
index 9ed3ae5fb..a7ef0be03 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/MissingOpenPgpKeyPairException.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/MissingOpenPgpKeyPairException.java
@@ -31,8 +31,9 @@ public class MissingOpenPgpKeyPairException extends Exception {
* Create a new {@link MissingOpenPgpKeyPairException}.
*
* @param owner owner of the missing key pair.
+ * @param e
*/
- public MissingOpenPgpKeyPairException(BareJid owner) {
+ public MissingOpenPgpKeyPairException(BareJid owner, Throwable e) {
super("Missing OpenPGP key pair for user " + owner);
this.owner = owner;
}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/MissingOpenPgpPublicKeyException.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/MissingOpenPgpPublicKeyException.java
index ddfec4b18..a68f8341a 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/MissingOpenPgpPublicKeyException.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/MissingOpenPgpPublicKeyException.java
@@ -27,8 +27,8 @@ public class MissingOpenPgpPublicKeyException extends Exception {
private static final long serialVersionUID = 1L;
- private final BareJid user;
- private final OpenPgpV4Fingerprint fingerprint;
+ private BareJid user;
+ private OpenPgpV4Fingerprint fingerprint;
/**
* Create a new {@link MissingOpenPgpPublicKeyException}.
@@ -42,6 +42,10 @@ public class MissingOpenPgpPublicKeyException extends Exception {
this.fingerprint = fingerprint;
}
+ public MissingOpenPgpPublicKeyException(Throwable e) {
+
+ }
+
/**
* Return the {@link BareJid} of the owner of the missing key.
*
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/MissingUserIdOnKeyException.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/MissingUserIdOnKeyException.java
new file mode 100644
index 000000000..59bb36d19
--- /dev/null
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/MissingUserIdOnKeyException.java
@@ -0,0 +1,12 @@
+package org.jivesoftware.smackx.ox.exception;
+
+import org.jxmpp.jid.BareJid;
+
+public class MissingUserIdOnKeyException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public MissingUserIdOnKeyException(BareJid owner, long keyId) {
+ super("Key " + Long.toHexString(keyId) + " does not have a user-id of \"xmpp:" + owner.toString() + "\".");
+ }
+}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/NoBackupFoundException.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/NoBackupFoundException.java
new file mode 100644
index 000000000..5e69a87e2
--- /dev/null
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/exception/NoBackupFoundException.java
@@ -0,0 +1,4 @@
+package org.jivesoftware.smackx.ox.exception;
+
+public class NoBackupFoundException {
+}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/listener/OpenPgpEncryptedMessageListener.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/listener/OpenPgpEncryptedMessageListener.java
index 654d3c0df..c62e0a7d0 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/listener/OpenPgpEncryptedMessageListener.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/listener/OpenPgpEncryptedMessageListener.java
@@ -17,7 +17,7 @@
package org.jivesoftware.smackx.ox.listener;
import org.jivesoftware.smack.packet.Message;
-import org.jivesoftware.smackx.ox.OpenPgpEncryptedChat;
+import org.jivesoftware.smackx.ox.chat.OpenPgpEncryptedChat;
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
import org.jivesoftware.smackx.ox.element.SigncryptElement;
@@ -36,8 +36,8 @@ public interface OpenPgpEncryptedMessageListener {
* @param decryptedPayload decrypted {@link SigncryptElement} which is carrying the payload.
* @param chat {@link OpenPgpEncryptedChat} which is the context of the message.
*/
- void newIncomingEncryptedMessage(EntityBareJid from,
- Message originalMessage,
- SigncryptElement decryptedPayload,
- OpenPgpEncryptedChat chat);
+ void newIncomingOxMessage(EntityBareJid from,
+ Message originalMessage,
+ SigncryptElement decryptedPayload,
+ OpenPgpEncryptedChat chat);
}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/DecryptedBytesAndMetadata.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/DecryptedBytesAndMetadata.java
new file mode 100644
index 000000000..ff480d7a3
--- /dev/null
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/DecryptedBytesAndMetadata.java
@@ -0,0 +1,30 @@
+package org.jivesoftware.smackx.ox.util;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class DecryptedBytesAndMetadata {
+
+ private final byte[] bytes;
+ private final Set verifiedSignatures;
+ private final Long decryptionKey;
+
+ public DecryptedBytesAndMetadata(byte[] bytes, Set verifiedSignatures, Long decryptionKey) {
+ this.bytes = bytes;
+ this.verifiedSignatures = verifiedSignatures;
+ this.decryptionKey = decryptionKey;
+ }
+
+ public byte[] getBytes() {
+ return Arrays.copyOf(bytes, bytes.length);
+ }
+
+ public Long getDecryptionKey() {
+ return decryptionKey;
+ }
+
+ public Set getVerifiedSignatures() {
+ return new HashSet<>(verifiedSignatures);
+ }
+}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/KeyBytesAndFingerprint.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/KeyBytesAndFingerprint.java
new file mode 100644
index 000000000..80044e7fc
--- /dev/null
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/KeyBytesAndFingerprint.java
@@ -0,0 +1,24 @@
+package org.jivesoftware.smackx.ox.util;
+
+import java.util.Arrays;
+
+import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
+
+public class KeyBytesAndFingerprint {
+
+ private final byte[] bytes;
+ private final OpenPgpV4Fingerprint fingerprint;
+
+ public KeyBytesAndFingerprint(byte[] bytes, OpenPgpV4Fingerprint fingerprint) {
+ this.bytes = bytes;
+ this.fingerprint = fingerprint;
+ }
+
+ public byte[] getBytes() {
+ return Arrays.copyOf(bytes, bytes.length);
+ }
+
+ public OpenPgpV4Fingerprint getFingerprint() {
+ return fingerprint;
+ }
+}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpInitializer.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpInitializer.java
similarity index 95%
rename from smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpInitializer.java
rename to smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpInitializer.java
index 33b64bab8..df90e6cdf 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpInitializer.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpInitializer.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jivesoftware.smackx.ox;
+package org.jivesoftware.smackx.ox.util;
import org.jivesoftware.smack.initializer.UrlInitializer;
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/PubSubDelegate.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/PubSubDelegate.java
similarity index 94%
rename from smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/PubSubDelegate.java
rename to smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/PubSubDelegate.java
index b08bef97d..c3de9f042 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/PubSubDelegate.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/PubSubDelegate.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jivesoftware.smackx.ox;
+package org.jivesoftware.smackx.ox.util;
import java.util.Date;
import java.util.List;
@@ -25,6 +25,7 @@ import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
import org.jivesoftware.smackx.ox.element.PubkeyElement;
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
@@ -72,7 +73,19 @@ public class PubSubDelegate {
return PEP_NODE_PUBLIC_KEYS + ":" + id;
}
-
+ /**
+ * Query the access model of {@code node}. If it is different from {@code accessModel}, change the access model
+ * of the node to {@code accessModel}.
+ *
+ * @see XEP-0060 §4.5 - Node Access Models
+ *
+ * @param node {@link LeafNode} whose PubSub access model we want to change
+ * @param accessModel new access model.
+ * @throws XMPPException.XMPPErrorException
+ * @throws SmackException.NotConnectedException
+ * @throws InterruptedException
+ * @throws SmackException.NoResponseException
+ */
public static void changeAccessModelIfNecessary(LeafNode node, AccessModel accessModel)
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException {
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/Util.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/Util.java
similarity index 93%
rename from smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/Util.java
rename to smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/Util.java
index 951ec856b..892c790fb 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/Util.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/Util.java
@@ -14,12 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jivesoftware.smackx.ox;
+package org.jivesoftware.smackx.ox.util;
import java.nio.ByteBuffer;
import java.util.Arrays;
import javax.xml.bind.DatatypeConverter;
+import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
+
public class Util {
/**