]> nmode's Git Repositories - signal-cli/blobdiff - lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java
Update libsignal-service
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / helper / PreKeyHelper.java
index e06ebbc16fba869804ccaef79031012c651a588c..bf7ad580bafde15e6668b5abfe7f2d066e0548dc 100644 (file)
@@ -11,27 +11,28 @@ import org.signal.libsignal.protocol.state.PreKeyRecord;
 import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.whispersystems.signalservice.api.NetworkResultUtil;
 import org.whispersystems.signalservice.api.account.PreKeyUpload;
+import org.whispersystems.signalservice.api.keys.OneTimePreKeyCounts;
 import org.whispersystems.signalservice.api.push.ServiceIdType;
 import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
-import org.whispersystems.signalservice.internal.push.OneTimePreKeyCounts;
+import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
 
 import java.io.IOException;
 import java.util.List;
 
 import static org.asamk.signal.manager.config.ServiceConfig.PREKEY_STALE_AGE;
 import static org.asamk.signal.manager.config.ServiceConfig.SIGNED_PREKEY_ROTATE_AGE;
+import static org.asamk.signal.manager.util.Utils.handleResponseException;
 
 public class PreKeyHelper {
 
-    private final static Logger logger = LoggerFactory.getLogger(PreKeyHelper.class);
+    private static final Logger logger = LoggerFactory.getLogger(PreKeyHelper.class);
 
     private final SignalAccount account;
     private final SignalDependencies dependencies;
 
-    public PreKeyHelper(
-            final SignalAccount account, final SignalDependencies dependencies
-    ) {
+    public PreKeyHelper(final SignalAccount account, final SignalDependencies dependencies) {
         this.account = account;
         this.dependencies = dependencies;
     }
@@ -41,6 +42,11 @@ public class PreKeyHelper {
         refreshPreKeysIfNecessary(ServiceIdType.PNI);
     }
 
+    public void forceRefreshPreKeys() throws IOException {
+        forceRefreshPreKeys(ServiceIdType.ACI);
+        forceRefreshPreKeys(ServiceIdType.PNI);
+    }
+
     public void refreshPreKeysIfNecessary(ServiceIdType serviceIdType) throws IOException {
         final var identityKeyPair = account.getIdentityKeyPair(serviceIdType);
         if (identityKeyPair == null) {
@@ -51,82 +57,146 @@ public class PreKeyHelper {
             return;
         }
 
+        if (refreshPreKeysIfNecessary(serviceIdType, identityKeyPair)) {
+            refreshPreKeysIfNecessary(serviceIdType, identityKeyPair);
+        }
+    }
+
+    public void forceRefreshPreKeys(ServiceIdType serviceIdType) throws IOException {
+        final var identityKeyPair = account.getIdentityKeyPair(serviceIdType);
+        if (identityKeyPair == null) {
+            return;
+        }
+        final var accountId = account.getAccountId(serviceIdType);
+        if (accountId == null) {
+            return;
+        }
+
+        final var counts = new OneTimePreKeyCounts(0, 0);
+        if (refreshPreKeysIfNecessary(serviceIdType, identityKeyPair, counts, true)) {
+            refreshPreKeysIfNecessary(serviceIdType, identityKeyPair, counts, true);
+        }
+    }
+
+    private boolean refreshPreKeysIfNecessary(
+            final ServiceIdType serviceIdType,
+            final IdentityKeyPair identityKeyPair
+    ) throws IOException {
         OneTimePreKeyCounts preKeyCounts;
         try {
-            preKeyCounts = dependencies.getAccountManager().getPreKeyCounts(serviceIdType);
+            preKeyCounts = handleResponseException(dependencies.getKeysApi().getAvailablePreKeyCounts(serviceIdType));
         } catch (AuthorizationFailedException e) {
             logger.debug("Failed to get pre key count, ignoring: " + e.getClass().getSimpleName());
             preKeyCounts = new OneTimePreKeyCounts(0, 0);
         }
 
-        SignedPreKeyRecord signedPreKeyRecord = null;
-        List<PreKeyRecord> preKeyRecords = null;
-        KyberPreKeyRecord lastResortKyberPreKeyRecord = null;
-        List<KyberPreKeyRecord> kyberPreKeyRecords = null;
+        return refreshPreKeysIfNecessary(serviceIdType, identityKeyPair, preKeyCounts, false);
+    }
 
-        try {
-            if (preKeyCounts.getEcCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) {
-                logger.debug("Refreshing {} ec pre keys, because only {} of min {} pre keys remain",
-                        serviceIdType,
-                        preKeyCounts.getEcCount(),
-                        ServiceConfig.PREKEY_MINIMUM_COUNT);
-                preKeyRecords = generatePreKeys(serviceIdType);
-            }
-            if (signedPreKeyNeedsRefresh(serviceIdType)) {
-                logger.debug("Refreshing {} signed pre key.", serviceIdType);
-                signedPreKeyRecord = generateSignedPreKey(serviceIdType, identityKeyPair);
-            }
-        } catch (Exception e) {
-            logger.warn("Failed to store new pre keys, resetting preKey id offset", e);
-            account.resetPreKeyOffsets(serviceIdType);
+    private boolean refreshPreKeysIfNecessary(
+            final ServiceIdType serviceIdType,
+            final IdentityKeyPair identityKeyPair,
+            final OneTimePreKeyCounts preKeyCounts,
+            final boolean force
+    ) throws IOException {
+        List<PreKeyRecord> preKeyRecords = null;
+        if (force || preKeyCounts.getEcCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) {
+            logger.debug("Refreshing {} ec pre keys, because only {} of min {} pre keys remain",
+                    serviceIdType,
+                    preKeyCounts.getEcCount(),
+                    ServiceConfig.PREKEY_MINIMUM_COUNT);
             preKeyRecords = generatePreKeys(serviceIdType);
+        }
+
+        SignedPreKeyRecord signedPreKeyRecord = null;
+        if (force || signedPreKeyNeedsRefresh(serviceIdType)) {
+            logger.debug("Refreshing {} signed pre key.", serviceIdType);
             signedPreKeyRecord = generateSignedPreKey(serviceIdType, identityKeyPair);
         }
 
+        List<KyberPreKeyRecord> kyberPreKeyRecords = null;
+        if (force || preKeyCounts.getKyberCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) {
+            logger.debug("Refreshing {} kyber pre keys, because only {} of min {} pre keys remain",
+                    serviceIdType,
+                    preKeyCounts.getKyberCount(),
+                    ServiceConfig.PREKEY_MINIMUM_COUNT);
+            kyberPreKeyRecords = generateKyberPreKeys(serviceIdType, identityKeyPair);
+        }
+
+        KyberPreKeyRecord lastResortKyberPreKeyRecord = null;
+        if (force || lastResortKyberPreKeyNeedsRefresh(serviceIdType)) {
+            logger.debug("Refreshing {} last resort kyber pre key.", serviceIdType);
+            lastResortKyberPreKeyRecord = generateLastResortKyberPreKey(serviceIdType,
+                    identityKeyPair,
+                    kyberPreKeyRecords == null ? 0 : kyberPreKeyRecords.size());
+        }
+
+        if (signedPreKeyRecord == null
+                && preKeyRecords == null
+                && lastResortKyberPreKeyRecord == null
+                && kyberPreKeyRecords == null) {
+            return false;
+        }
+
+        final var preKeyUpload = new PreKeyUpload(serviceIdType,
+                signedPreKeyRecord,
+                preKeyRecords,
+                lastResortKyberPreKeyRecord,
+                kyberPreKeyRecords);
+        var needsReset = false;
         try {
-            if (preKeyCounts.getKyberCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) {
-                logger.debug("Refreshing {} kyber pre keys, because only {} of min {} pre keys remain",
-                        serviceIdType,
-                        preKeyCounts.getKyberCount(),
-                        ServiceConfig.PREKEY_MINIMUM_COUNT);
-                kyberPreKeyRecords = generateKyberPreKeys(serviceIdType, identityKeyPair);
+            NetworkResultUtil.toPreKeysLegacy(dependencies.getKeysApi().setPreKeys(preKeyUpload));
+            try {
+                if (preKeyRecords != null) {
+                    account.addPreKeys(serviceIdType, preKeyRecords);
+                }
+                if (signedPreKeyRecord != null) {
+                    account.addSignedPreKey(serviceIdType, signedPreKeyRecord);
+                }
+            } catch (Exception e) {
+                logger.warn("Failed to store new pre keys, resetting preKey id offset", e);
+                account.resetPreKeyOffsets(serviceIdType);
+                needsReset = true;
             }
-            if (lastResortKyberPreKeyNeedsRefresh(serviceIdType)) {
-                logger.debug("Refreshing {} last resort kyber pre key.", serviceIdType);
-                lastResortKyberPreKeyRecord = generateLastResortKyberPreKey(serviceIdType, identityKeyPair);
+            try {
+                if (kyberPreKeyRecords != null) {
+                    account.addKyberPreKeys(serviceIdType, kyberPreKeyRecords);
+                }
+                if (lastResortKyberPreKeyRecord != null) {
+                    account.addLastResortKyberPreKey(serviceIdType, lastResortKyberPreKeyRecord);
+                }
+            } catch (Exception e) {
+                logger.warn("Failed to store new kyber pre keys, resetting preKey id offset", e);
+                account.resetKyberPreKeyOffsets(serviceIdType);
+                needsReset = true;
             }
-        } catch (Exception e) {
-            logger.warn("Failed to store new kyber pre keys, resetting preKey id offset", e);
-            account.resetKyberPreKeyOffsets(serviceIdType);
-            kyberPreKeyRecords = generateKyberPreKeys(serviceIdType, identityKeyPair);
-            lastResortKyberPreKeyRecord = generateLastResortKyberPreKey(serviceIdType, identityKeyPair);
+        } catch (AuthorizationFailedException e) {
+            // This can happen when the primary device has changed phone number
+            logger.warn("Failed to updated pre keys: {}", e.getMessage());
+        } catch (NonSuccessfulResponseCodeException e) {
+            if (serviceIdType != ServiceIdType.PNI || e.code != 422) {
+                throw e;
+            }
+            logger.warn("Failed to set PNI pre keys, ignoring for now. Account needs to be reregistered to fix this.");
         }
+        return needsReset;
+    }
 
-        if (signedPreKeyRecord != null
-                || preKeyRecords != null
-                || lastResortKyberPreKeyRecord != null
-                || kyberPreKeyRecords != null) {
-            final var preKeyUpload = new PreKeyUpload(serviceIdType,
-                    identityKeyPair.getPublicKey(),
-                    signedPreKeyRecord,
-                    preKeyRecords,
-                    lastResortKyberPreKeyRecord,
-                    kyberPreKeyRecords);
-            dependencies.getAccountManager().setPreKeys(preKeyUpload);
-        }
+    public void cleanOldPreKeys() {
+        cleanOldPreKeys(ServiceIdType.ACI);
+        cleanOldPreKeys(ServiceIdType.PNI);
+    }
 
-        cleanSignedPreKeys((serviceIdType));
+    private void cleanOldPreKeys(final ServiceIdType serviceIdType) {
+        cleanSignedPreKeys(serviceIdType);
         cleanOneTimePreKeys(serviceIdType);
     }
 
     private List<PreKeyRecord> generatePreKeys(ServiceIdType serviceIdType) {
         final var accountData = account.getAccountData(serviceIdType);
-        final var offset = accountData.getPreKeyMetadata().getPreKeyIdOffset();
-
-        var records = KeyUtils.generatePreKeyRecords(offset);
-        account.addPreKeys(serviceIdType, records);
+        final var offset = accountData.getPreKeyMetadata().getNextPreKeyId();
 
-        return records;
+        return KeyUtils.generatePreKeyRecords(offset);
     }
 
     private boolean signedPreKeyNeedsRefresh(ServiceIdType serviceIdType) {
@@ -148,22 +218,17 @@ public class PreKeyHelper {
         final var accountData = account.getAccountData(serviceIdType);
         final var signedPreKeyId = accountData.getPreKeyMetadata().getNextSignedPreKeyId();
 
-        var record = KeyUtils.generateSignedPreKeyRecord(signedPreKeyId, identityKeyPair);
-        account.addSignedPreKey(serviceIdType, record);
-
-        return record;
+        return KeyUtils.generateSignedPreKeyRecord(signedPreKeyId, identityKeyPair.getPrivateKey());
     }
 
     private List<KyberPreKeyRecord> generateKyberPreKeys(
-            ServiceIdType serviceIdType, final IdentityKeyPair identityKeyPair
+            ServiceIdType serviceIdType,
+            final IdentityKeyPair identityKeyPair
     ) {
         final var accountData = account.getAccountData(serviceIdType);
-        final var offset = accountData.getPreKeyMetadata().getKyberPreKeyIdOffset();
-
-        var records = KeyUtils.generateKyberPreKeyRecords(offset, identityKeyPair.getPrivateKey());
-        account.addKyberPreKeys(serviceIdType, records);
+        final var offset = accountData.getPreKeyMetadata().getNextKyberPreKeyId();
 
-        return records;
+        return KeyUtils.generateKyberPreKeyRecords(offset, identityKeyPair.getPrivateKey());
     }
 
     private boolean lastResortKyberPreKeyNeedsRefresh(ServiceIdType serviceIdType) {
@@ -183,15 +248,14 @@ public class PreKeyHelper {
     }
 
     private KyberPreKeyRecord generateLastResortKyberPreKey(
-            ServiceIdType serviceIdType, IdentityKeyPair identityKeyPair
+            ServiceIdType serviceIdType,
+            IdentityKeyPair identityKeyPair,
+            final int offset
     ) {
         final var accountData = account.getAccountData(serviceIdType);
-        final var signedPreKeyId = accountData.getPreKeyMetadata().getKyberPreKeyIdOffset();
-
-        var record = KeyUtils.generateKyberPreKeyRecord(signedPreKeyId, identityKeyPair.getPrivateKey());
-        account.addLastResortKyberPreKey(serviceIdType, record);
+        final var signedPreKeyId = accountData.getPreKeyMetadata().getNextKyberPreKeyId() + offset;
 
-        return record;
+        return KeyUtils.generateKyberPreKeyRecord(signedPreKeyId, identityKeyPair.getPrivateKey());
     }
 
     private void cleanSignedPreKeys(ServiceIdType serviceIdType) {