diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
index bbb104fe8..246f05eb2 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
@@ -514,6 +514,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// Check if not already connected
throwAlreadyConnectedExceptionIfAppropriate();
+ // Notify connection listeners that we are trying to connect
+ callConnectionConnectingListener();
+
// Reset the connection state
initState();
closingStreamReceived = false;
@@ -1680,6 +1683,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
}
}
+ protected void callConnectionConnectingListener() {
+ for (ConnectionListener listener : connectionListeners) {
+ listener.connecting(this);
+ }
+ }
+
protected void callConnectionConnectedListener() {
for (ConnectionListener listener : connectionListeners) {
listener.connected(this);
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionListener.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionListener.java
index 3e85191b9..304c8c75a 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionListener.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionListener.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2003-2007 Jive Software.
+ * Copyright 2003-2007 Jive Software, 2020 Paul Schaub
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,16 @@ package org.jivesoftware.smack;
*/
public interface ConnectionListener {
+ /**
+ * Notification that the connection is in the process of connecting.
+ * This method is called when {@link AbstractXMPPConnection#connect()} is executed.
+ *
+ * @param connection connection
+ * @since 4.4
+ */
+ default void connecting(XMPPConnection connection) {
+ }
+
/**
* Notification that the connection has been successfully connected to the remote endpoint (e.g. the XMPP server).
*
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
index c966b2d34..77bc402c3 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
@@ -270,7 +270,12 @@ public class StringUtils {
/**
* 24 upper case characters from the latin alphabet and numbers without '0' and 'O'.
*/
- private static final char[] UNAMBIGUOUS_NUMBERS_AND_LETTER = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ".toCharArray();
+ public static final String UNAMBIGUOUS_NUMBERS_AND_LETTERS_STRING = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ";
+
+ /**
+ * 24 upper case characters from the latin alphabet and numbers without '0' and 'O'.
+ */
+ private static final char[] UNAMBIGUOUS_NUMBERS_AND_LETTERS = UNAMBIGUOUS_NUMBERS_AND_LETTERS_STRING.toCharArray();
/**
* Returns a random String of numbers and letters (lower and upper case)
@@ -294,14 +299,14 @@ public class StringUtils {
// See also https://www.grc.com/haystack.htm
final int REQUIRED_LENGTH = 10;
- return randomString(RandomUtil.SECURE_RANDOM.get(), UNAMBIGUOUS_NUMBERS_AND_LETTER, REQUIRED_LENGTH);
+ return randomString(RandomUtil.SECURE_RANDOM.get(), UNAMBIGUOUS_NUMBERS_AND_LETTERS, REQUIRED_LENGTH);
}
public static String secureUniqueRandomString() {
// 34^13 = 8.11e19 possible combinations, which is > 2^64.
final int REQUIRED_LENGTH = 13;
- return randomString(RandomUtil.SECURE_RANDOM.get(), UNAMBIGUOUS_NUMBERS_AND_LETTER, REQUIRED_LENGTH);
+ return randomString(RandomUtil.SECURE_RANDOM.get(), UNAMBIGUOUS_NUMBERS_AND_LETTERS, REQUIRED_LENGTH);
}
/**
@@ -324,7 +329,7 @@ public class StringUtils {
// See also https://www.grc.com/haystack.htm
final int REQUIRED_LENGTH = 24;
- return randomString(RandomUtil.SECURE_RANDOM.get(), UNAMBIGUOUS_NUMBERS_AND_LETTER, REQUIRED_LENGTH);
+ return randomString(RandomUtil.SECURE_RANDOM.get(), UNAMBIGUOUS_NUMBERS_AND_LETTERS, REQUIRED_LENGTH);
}
private static final int RANDOM_STRING_CHUNK_SIZE = 4;
@@ -368,8 +373,8 @@ public class StringUtils {
char[] randomChars = new char[length];
for (int i = 0; i < length; i++) {
- int index = random.nextInt(UNAMBIGUOUS_NUMBERS_AND_LETTER.length);
- randomChars[i] = UNAMBIGUOUS_NUMBERS_AND_LETTER[index];
+ int index = random.nextInt(UNAMBIGUOUS_NUMBERS_AND_LETTERS.length);
+ randomChars[i] = UNAMBIGUOUS_NUMBERS_AND_LETTERS[index];
}
return new String(randomChars);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java
index ba8d378bb..8031301ad 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java
@@ -16,6 +16,7 @@
*/
package org.jivesoftware.smackx.ox;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -26,17 +27,11 @@ import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
-import java.util.Arrays;
-import java.util.Set;
import java.util.logging.Level;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.util.StringUtils;
-
-import org.jivesoftware.smackx.ox.callback.backup.AskForBackupCodeCallback;
-import org.jivesoftware.smackx.ox.callback.backup.DisplayBackupCodeCallback;
-import org.jivesoftware.smackx.ox.callback.backup.SecretKeyBackupSelectionCallback;
import org.jivesoftware.smackx.ox.crypto.PainlessOpenPgpProvider;
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyException;
@@ -64,10 +59,6 @@ public class OXSecretKeyBackupIntegrationTest extends AbstractOpenPgpIntegration
private static final File beforePath = new File(tempDir, "ox_backup_" + sessionId);
private static final File afterPath = new File(tempDir, "ox_restore_" + sessionId);
- private String backupCode = null;
-
- private OpenPgpManager openPgpManager;
-
/**
* This integration test tests the basic secret key backup and restore functionality as described
* in XEP-0373 §5.
@@ -123,7 +114,7 @@ public class OXSecretKeyBackupIntegrationTest extends AbstractOpenPgpIntegration
OpenPgpStore beforeStore = new FileBasedOpenPgpStore(beforePath);
beforeStore.setKeyRingProtector(new UnprotectedKeysProtector());
PainlessOpenPgpProvider beforeProvider = new PainlessOpenPgpProvider(beforeStore);
- openPgpManager = OpenPgpManager.getInstanceFor(aliceConnection);
+ OpenPgpManager openPgpManager = OpenPgpManager.getInstanceFor(aliceConnection);
openPgpManager.setOpenPgpProvider(beforeProvider);
OpenPgpSelf self = openPgpManager.getOpenPgpSelf();
@@ -141,29 +132,15 @@ public class OXSecretKeyBackupIntegrationTest extends AbstractOpenPgpIntegration
PGPPublicKeyRing beforePub = beforeStore.getPublicKeyRing(alice, keyFingerprint);
assertNotNull(beforePub);
- openPgpManager.backupSecretKeyToServer(new DisplayBackupCodeCallback() {
- @Override
- public void displayBackupCode(String backupCode) {
- OXSecretKeyBackupIntegrationTest.this.backupCode = backupCode;
- }
- }, new SecretKeyBackupSelectionCallback() {
- @Override
- public Set selectKeysToBackup(Set availableSecretKeys) {
- return availableSecretKeys;
- }
- });
+ OpenPgpSecretKeyBackupPassphrase backupPassphrase =
+ openPgpManager.backupSecretKeyToServer(availableSecretKeys -> availableSecretKeys);
FileBasedOpenPgpStore afterStore = new FileBasedOpenPgpStore(afterPath);
afterStore.setKeyRingProtector(new UnprotectedKeysProtector());
PainlessOpenPgpProvider afterProvider = new PainlessOpenPgpProvider(afterStore);
openPgpManager.setOpenPgpProvider(afterProvider);
- OpenPgpV4Fingerprint fingerprint = openPgpManager.restoreSecretKeyServerBackup(new AskForBackupCodeCallback() {
- @Override
- public String askForBackupCode() {
- return backupCode;
- }
- });
+ OpenPgpV4Fingerprint fingerprint = openPgpManager.restoreSecretKeyServerBackup(() -> backupPassphrase);
assertEquals(keyFingerprint, fingerprint);
@@ -173,10 +150,10 @@ public class OXSecretKeyBackupIntegrationTest extends AbstractOpenPgpIntegration
PGPSecretKeyRing afterSec = afterStore.getSecretKeyRing(alice, keyFingerprint);
assertNotNull(afterSec);
- assertTrue(Arrays.equals(beforeSec.getEncoded(), afterSec.getEncoded()));
+ assertArrayEquals(beforeSec.getEncoded(), afterSec.getEncoded());
PGPPublicKeyRing afterPub = afterStore.getPublicKeyRing(alice, keyFingerprint);
assertNotNull(afterPub);
- assertTrue(Arrays.equals(beforePub.getEncoded(), afterPub.getEncoded()));
+ assertArrayEquals(beforePub.getEncoded(), afterPub.getEncoded());
}
}
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 ce49dbde6..61c86b61f 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
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2017-2020 Florian Schmaus, 2018 Paul Schaub.
+ * Copyright 2018-2020 Paul Schaub, 2017-2020 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import static org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil.PEP_NODE_PUBLIC_
import static org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil.PEP_NODE_PUBLIC_KEYS_NOTIFY;
import static org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil.publishPublicKey;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
@@ -43,10 +42,8 @@ import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smack.xml.XmlPullParserException;
-
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.ox.callback.backup.AskForBackupCodeCallback;
-import org.jivesoftware.smackx.ox.callback.backup.DisplayBackupCodeCallback;
import org.jivesoftware.smackx.ox.callback.backup.SecretKeyBackupSelectionCallback;
import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider;
import org.jivesoftware.smackx.ox.element.CryptElement;
@@ -76,12 +73,9 @@ import org.jivesoftware.smackx.pubsub.PubSubException;
import org.jivesoftware.smackx.pubsub.PubSubFeature;
import org.bouncycastle.openpgp.PGPException;
-import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
-import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
-import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid;
import org.pgpainless.key.OpenPgpV4Fingerprint;
@@ -389,8 +383,9 @@ public final class OpenPgpManager extends Manager {
*
* @see XEP-0373 §5
*
- * @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.
+ * @return secret key passphrase used to encrypt the backup.
+ *
* @throws InterruptedException if the thread is interrupted.
* @throws PubSubException.NotALeafNodeException if the private node is not a {@link LeafNode}.
* @throws XMPPException.XMPPErrorException in case of an XMPP protocol error.
@@ -402,8 +397,38 @@ public final class OpenPgpManager extends Manager {
* @throws PGPException PGP is brittle
* @throws MissingOpenPgpKeyException in case we have no OpenPGP key pair to back up.
*/
- public void backupSecretKeyToServer(DisplayBackupCodeCallback displayCodeCallback,
- SecretKeyBackupSelectionCallback selectKeyCallback)
+ public OpenPgpSecretKeyBackupPassphrase backupSecretKeyToServer(SecretKeyBackupSelectionCallback selectKeyCallback)
+ throws InterruptedException, PubSubException.NotALeafNodeException,
+ XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException,
+ SmackException.NotLoggedInException, IOException,
+ SmackException.FeatureNotSupportedException, PGPException, MissingOpenPgpKeyException {
+ OpenPgpSecretKeyBackupPassphrase passphrase = SecretKeyBackupHelper.generateBackupPassword();
+ backupSecretKeyToServer(selectKeyCallback, passphrase);
+ return passphrase;
+ }
+
+ /**
+ * Upload the encrypted secret key to a private PEP node.
+ * The backup is encrypted using the provided secret key passphrase.
+ *
+ * @see XEP-0373 §5
+ *
+ * @param selectKeyCallback callback, which will receive the users choice of which keys will be backed up. @param selectKeyCallback
+ * @param passphrase secret key passphrase
+ *
+ * @throws InterruptedException if the thread is interrupted.
+ * @throws PubSubException.NotALeafNodeException if the private node is not a {@link LeafNode}.
+ * @throws XMPPException.XMPPErrorException in case of an XMPP protocol error.
+ * @throws SmackException.NotConnectedException if we are not connected.
+ * @throws SmackException.NoResponseException if the server doesn't respond.
+ * @throws SmackException.NotLoggedInException if we are not logged in.
+ * @throws IOException IO is dangerous.
+ * @throws SmackException.FeatureNotSupportedException if the server doesn't support the PubSub whitelist access model.
+ * @throws PGPException PGP is brittle
+ * @throws MissingOpenPgpKeyException in case we have no OpenPGP key pair to back up.
+ */
+ public void backupSecretKeyToServer(SecretKeyBackupSelectionCallback selectKeyCallback,
+ OpenPgpSecretKeyBackupPassphrase passphrase)
throws InterruptedException, PubSubException.NotALeafNodeException,
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException,
SmackException.NotLoggedInException, IOException,
@@ -413,8 +438,6 @@ public final class OpenPgpManager extends Manager {
BareJid ownJid = connection().getUser().asBareJid();
- String backupCode = SecretKeyBackupHelper.generateBackupPassword();
-
PGPSecretKeyRingCollection secretKeyRings = provider.getStore().getSecretKeysOf(ownJid);
Set availableKeyPairs = new HashSet<>();
@@ -424,10 +447,9 @@ public final class OpenPgpManager extends Manager {
Set selectedKeyPairs = selectKeyCallback.selectKeysToBackup(availableKeyPairs);
- SecretkeyElement secretKey = SecretKeyBackupHelper.createSecretkeyElement(provider, ownJid, selectedKeyPairs, backupCode);
+ SecretkeyElement secretKey = SecretKeyBackupHelper.createSecretkeyElement(provider, ownJid, selectedKeyPairs, passphrase);
OpenPgpPubSubUtil.depositSecretKey(connection(), secretKey);
- displayCodeCallback.displayBackupCode(backupCode);
}
/**
@@ -476,19 +498,14 @@ public final class OpenPgpManager extends Manager {
throw new NoBackupFoundException();
}
- String backupCode = codeCallback.askForBackupCode();
+ OpenPgpSecretKeyBackupPassphrase backupCode = codeCallback.askForBackupCode();
PGPSecretKeyRing secretKeys = SecretKeyBackupHelper.restoreSecretKeyBackup(backup, backupCode);
+ OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(secretKeys);
provider.getStore().importSecretKey(getJidOrThrow(), secretKeys);
provider.getStore().importPublicKey(getJidOrThrow(), BCUtil.publicKeyRingFromSecretKeyRing(secretKeys));
- ByteArrayOutputStream buffer = new ByteArrayOutputStream(2048);
- for (PGPSecretKey sk : secretKeys) {
- PGPPublicKey pk = sk.getPublicKey();
- if (pk != null) pk.encode(buffer);
- }
- PGPPublicKeyRing publicKeys = new PGPPublicKeyRing(buffer.toByteArray(), new BcKeyFingerprintCalculator());
- provider.getStore().importPublicKey(getJidOrThrow(), publicKeys);
+ getOpenPgpSelf().trust(fingerprint);
return new OpenPgpV4Fingerprint(secretKeys);
}
@@ -551,7 +568,7 @@ public final class OpenPgpManager extends Manager {
if (contentElement instanceof SigncryptElement) {
for (SigncryptElementReceivedListener l : signcryptElementReceivedListeners) {
l.signcryptElementReceived(contact, message, (SigncryptElement) contentElement,
- decrypted.getMetadata());
+ decrypted.getMetadata());
}
return;
}
@@ -566,7 +583,7 @@ public final class OpenPgpManager extends Manager {
if (contentElement instanceof CryptElement) {
for (CryptElementReceivedListener l : cryptElementReceivedListeners) {
l.cryptElementReceived(contact, message, (CryptElement) contentElement,
- decrypted.getMetadata());
+ decrypted.getMetadata());
}
return;
}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpSecretKeyBackupPassphrase.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpSecretKeyBackupPassphrase.java
new file mode 100644
index 000000000..cc96bbd4f
--- /dev/null
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpSecretKeyBackupPassphrase.java
@@ -0,0 +1,64 @@
+/**
+ *
+ * Copyright 2020 Paul Schaub.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.ox;
+
+import static org.jivesoftware.smack.util.StringUtils.UNAMBIGUOUS_NUMBERS_AND_LETTERS_STRING;
+
+import java.util.regex.Pattern;
+
+/**
+ * Represents a secret key backup passphrase whose format is described in XEP-0373 §5.3.
+ *
+ * @see
+ * XEP-0373 §5.4 Encrypting the Secret Key Backup
+ */
+public class OpenPgpSecretKeyBackupPassphrase implements CharSequence {
+
+ private static final Pattern PASSPHRASE_PATTERN = Pattern.compile(
+ "^([" + UNAMBIGUOUS_NUMBERS_AND_LETTERS_STRING + "]{4}-){5}" +
+ "[" + UNAMBIGUOUS_NUMBERS_AND_LETTERS_STRING + "]{4}$");
+
+ private final String passphrase;
+
+ public OpenPgpSecretKeyBackupPassphrase(String passphrase) {
+ if (!PASSPHRASE_PATTERN.matcher(passphrase).matches()) {
+ throw new IllegalArgumentException("Passphrase must be 24 upper case letters and numbers from the english " +
+ "alphabet without 'O' and '0', divided into blocks of 4 and separated with dashes ('-').");
+ }
+ this.passphrase = passphrase;
+ }
+
+ @Override
+ public int length() {
+ return passphrase.length();
+ }
+
+ @Override
+ public char charAt(int i) {
+ return passphrase.charAt(i);
+ }
+
+ @Override
+ public CharSequence subSequence(int i, int i1) {
+ return passphrase.subSequence(i, i1);
+ }
+
+ @Override
+ public String toString() {
+ return passphrase;
+ }
+}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/backup/AskForBackupCodeCallback.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/backup/AskForBackupCodeCallback.java
index 4a6653012..5d7a214bc 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/backup/AskForBackupCodeCallback.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/backup/AskForBackupCodeCallback.java
@@ -16,6 +16,8 @@
*/
package org.jivesoftware.smackx.ox.callback.backup;
+import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
+
public interface AskForBackupCodeCallback {
/**
@@ -27,5 +29,5 @@ public interface AskForBackupCodeCallback {
*
* @return backup code provided by the user.
*/
- String askForBackupCode();
+ OpenPgpSecretKeyBackupPassphrase askForBackupCode();
}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/backup/DisplayBackupCodeCallback.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/backup/DisplayBackupCodeCallback.java
deleted file mode 100644
index bc41a92bb..000000000
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/callback/backup/DisplayBackupCodeCallback.java
+++ /dev/null
@@ -1,32 +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.callback.backup;
-
-public interface DisplayBackupCodeCallback {
-
- /**
- * This method is used to provide a client access to the generated backup code.
- * The client can then go ahead and display the code to the user.
- * The backup code follows the format described in XEP-0373 §5.3
- *
- * @see
- * XEP-0373 §5.4 Encrypting the Secret Key Backup
- *
- * @param backupCode backup code
- */
- void displayBackupCode(String backupCode);
-}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java
index b36ca19f3..0e418d80b 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java
@@ -111,7 +111,7 @@ public class OpenPgpPubSubUtil {
* Publish the users OpenPGP public key to the public key node if necessary.
* Also announce the key to other users by updating the metadata node.
*
- * @see XEP-0373 §4.1
+ * @see XEP-0373 §4.1
*
* @param pepManager The PEP manager.
* @param pubkeyElement {@link PubkeyElement} containing the public key
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java
index 59f0fd56e..978f22eb4 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java
@@ -23,6 +23,7 @@ import java.util.Set;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.stringencoder.Base64;
+import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider;
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
@@ -51,8 +52,8 @@ public class SecretKeyBackupHelper {
*
* @return backup code
*/
- public static String generateBackupPassword() {
- return StringUtils.secureOfflineAttackSafeRandomString();
+ public static OpenPgpSecretKeyBackupPassphrase generateBackupPassword() {
+ return new OpenPgpSecretKeyBackupPassphrase(StringUtils.secureOfflineAttackSafeRandomString());
}
/**
@@ -73,7 +74,7 @@ public class SecretKeyBackupHelper {
public static SecretkeyElement createSecretkeyElement(OpenPgpProvider provider,
BareJid owner,
Set fingerprints,
- String backupCode)
+ OpenPgpSecretKeyBackupPassphrase backupCode)
throws PGPException, IOException, MissingOpenPgpKeyException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@@ -105,9 +106,9 @@ public class SecretKeyBackupHelper {
* @throws IOException IO is dangerous
*/
public static SecretkeyElement createSecretkeyElement(byte[] keys,
- String backupCode)
+ OpenPgpSecretKeyBackupPassphrase backupCode)
throws PGPException, IOException {
- byte[] encrypted = PGPainless.encryptWithPassword(keys, new Passphrase(backupCode.toCharArray()),
+ byte[] encrypted = PGPainless.encryptWithPassword(keys, new Passphrase(backupCode.toString().toCharArray()),
SymmetricKeyAlgorithm.AES_256);
return new SecretkeyElement(Base64.encode(encrypted));
}
@@ -123,13 +124,13 @@ public class SecretKeyBackupHelper {
* @throws IOException IO is dangerous.
* @throws PGPException PGP is brittle.
*/
- public static PGPSecretKeyRing restoreSecretKeyBackup(SecretkeyElement backup, String backupCode)
+ public static PGPSecretKeyRing restoreSecretKeyBackup(SecretkeyElement backup, OpenPgpSecretKeyBackupPassphrase backupCode)
throws InvalidBackupCodeException, IOException, PGPException {
byte[] encrypted = Base64.decode(backup.getB64Data());
byte[] decrypted;
try {
- decrypted = PGPainless.decryptWithPassword(encrypted, new Passphrase(backupCode.toCharArray()));
+ decrypted = PGPainless.decryptWithPassword(encrypted, new Passphrase(backupCode.toString().toCharArray()));
} catch (IOException | PGPException e) {
throw new InvalidBackupCodeException("Could not decrypt secret key backup. Possibly wrong passphrase?", e);
}
diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java
index 4383a02b3..c9fc021eb 100644
--- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java
+++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java
@@ -264,7 +264,7 @@ public final class OXInstantMessagingManager extends Manager {
* Add an OX-IM message element to a message.
*
* @param messageBuilder message
- * @param contacts recipients of the message
+ * @param recipients recipients of the message
* @param payload payload which will be encrypted and signed
*
* @return metadata about the messages encryption + signatures.
@@ -273,13 +273,9 @@ public final class OXInstantMessagingManager extends Manager {
* @throws PGPException in case something goes wrong during encryption
* @throws IOException IO is dangerous (we need to read keys)
*/
- public OpenPgpMetadata addOxMessage(MessageBuilder messageBuilder, Set contacts, List payload)
+ public OpenPgpMetadata addOxMessage(MessageBuilder messageBuilder, Set recipients, List payload)
throws SmackException.NotLoggedInException, IOException, PGPException {
- HashSet recipients = new HashSet<>(contacts);
- OpenPgpContact self = openPgpManager.getOpenPgpSelf();
- recipients.add(self);
-
OpenPgpElementAndMetadata openPgpElementAndMetadata = signAndEncrypt(recipients, payload);
messageBuilder.addExtension(openPgpElementAndMetadata.getElement());
diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpSecretKeyBackupPassphraseTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpSecretKeyBackupPassphraseTest.java
new file mode 100644
index 000000000..3c910a006
--- /dev/null
+++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpSecretKeyBackupPassphraseTest.java
@@ -0,0 +1,57 @@
+/**
+ *
+ * Copyright 2020 Paul Schaub.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.ox;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+import org.jivesoftware.smackx.ox.util.SecretKeyBackupHelper;
+
+import org.junit.jupiter.api.Test;
+
+public class OpenPgpSecretKeyBackupPassphraseTest {
+
+ @Test
+ public void secretKeyPassphraseConstructorTest() {
+ OpenPgpSecretKeyBackupPassphrase valid =
+ new OpenPgpSecretKeyBackupPassphrase("TWNK-KD5Y-MT3T-E1GS-DRDB-KVTW");
+
+ assertNotNull(valid);
+ for (int i = 0; i < 50; i++) {
+ assertNotNull(SecretKeyBackupHelper.generateBackupPassword());
+ }
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new OpenPgpSecretKeyBackupPassphrase("TWNKKD5YMT3TE1GSDRDBKVTW"));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new OpenPgpSecretKeyBackupPassphrase("0123-4567-89AB-CDEF-GHIJ-KLMN"));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new OpenPgpSecretKeyBackupPassphrase("CONT-AINS-ILLE-GALL-ETTE-RSO0"));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new OpenPgpSecretKeyBackupPassphrase("TWNK-KD5Y-MT3T-E1GS-DRDB-"));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new OpenPgpSecretKeyBackupPassphrase("TWNK-KD5Y-MT3T-E1GS-DRDB-KVTW-ADDD"));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new OpenPgpSecretKeyBackupPassphrase("TWNK KD5Y MT3T E1GS DRDB KVTW"));
+
+ }
+}
diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/SecretKeyBackupHelperTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/SecretKeyBackupHelperTest.java
index e7acc3201..ceaf50638 100644
--- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/SecretKeyBackupHelperTest.java
+++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/SecretKeyBackupHelperTest.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2018 Paul Schaub.
+ * Copyright 2018-2020 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,17 +18,16 @@ package org.jivesoftware.smackx.ox;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertArrayEquals;
import java.io.File;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
-import java.util.Arrays;
import java.util.Collections;
import org.jivesoftware.smack.test.util.SmackTestSuite;
-
import org.jivesoftware.smackx.ox.crypto.PainlessOpenPgpProvider;
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
@@ -60,7 +59,7 @@ public class SecretKeyBackupHelperTest extends SmackTestSuite {
public void backupPasswordGenerationTest() {
final String alphabet = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ";
- String backupCode = SecretKeyBackupHelper.generateBackupPassword();
+ OpenPgpSecretKeyBackupPassphrase backupCode = SecretKeyBackupHelper.generateBackupPassword();
assertEquals(29, backupCode.length());
for (int i = 0; i < backupCode.length(); i++) {
if ((i + 1) % 5 == 0) {
@@ -86,12 +85,13 @@ public class SecretKeyBackupHelperTest extends SmackTestSuite {
provider.getStore().importSecretKey(jid, keyRing.getSecretKeys());
// Create encrypted backup
- String backupCode = SecretKeyBackupHelper.generateBackupPassword();
- SecretkeyElement element = SecretKeyBackupHelper.createSecretkeyElement(provider, jid, Collections.singleton(new OpenPgpV4Fingerprint(keyRing.getSecretKeys())), backupCode);
+ OpenPgpSecretKeyBackupPassphrase backupCode = SecretKeyBackupHelper.generateBackupPassword();
+ SecretkeyElement element = SecretKeyBackupHelper.createSecretkeyElement(provider, jid,
+ Collections.singleton(new OpenPgpV4Fingerprint(keyRing.getSecretKeys())), backupCode);
// Decrypt backup and compare
PGPSecretKeyRing secretKeyRing = SecretKeyBackupHelper.restoreSecretKeyBackup(element, backupCode);
- assertTrue(Arrays.equals(keyRing.getSecretKeys().getEncoded(), secretKeyRing.getEncoded()));
+ assertArrayEquals(keyRing.getSecretKeys().getEncoded(), secretKeyRing.getEncoded());
}
@AfterClass