Search in sources :

Example 6 with SignalStorageManifest

use of org.whispersystems.signalservice.api.storage.SignalStorageManifest in project Signal-Android by WhisperSystems.

the class SignalServiceAccountManager method getStorageManifest.

public Optional<SignalStorageManifest> getStorageManifest(StorageKey storageKey) throws IOException {
    try {
        String authToken = this.pushServiceSocket.getStorageAuth();
        StorageManifest storageManifest = this.pushServiceSocket.getStorageManifest(authToken);
        return Optional.of(SignalStorageModels.remoteToLocalStorageManifest(storageManifest, storageKey));
    } catch (InvalidKeyException | NotFoundException e) {
        Log.w(TAG, "Error while fetching manifest.", e);
        return Optional.absent();
    }
}
Also used : NotFoundException(org.whispersystems.signalservice.api.push.exceptions.NotFoundException) ByteString(com.google.protobuf.ByteString) InvalidKeyException(org.whispersystems.libsignal.InvalidKeyException) StorageManifest(org.whispersystems.signalservice.internal.storage.protos.StorageManifest) SignalStorageManifest(org.whispersystems.signalservice.api.storage.SignalStorageManifest)

Example 7 with SignalStorageManifest

use of org.whispersystems.signalservice.api.storage.SignalStorageManifest in project Signal-Android by WhisperSystems.

the class StorageAccountRestoreJob method onRun.

@Override
protected void onRun() throws Exception {
    SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
    StorageKey storageServiceKey = SignalStore.storageService().getOrCreateStorageKey();
    Log.i(TAG, "Retrieving manifest...");
    Optional<SignalStorageManifest> manifest = accountManager.getStorageManifest(storageServiceKey);
    if (!manifest.isPresent()) {
        Log.w(TAG, "Manifest did not exist or was undecryptable (bad key). Not restoring. Force-pushing.");
        ApplicationDependencies.getJobManager().add(new StorageForcePushJob());
        return;
    }
    Log.i(TAG, "Resetting the local manifest to an empty state so that it will sync later.");
    SignalStore.storageService().setManifest(SignalStorageManifest.EMPTY);
    Optional<StorageId> accountId = manifest.get().getAccountStorageId();
    if (!accountId.isPresent()) {
        Log.w(TAG, "Manifest had no account record! Not restoring.");
        return;
    }
    Log.i(TAG, "Retrieving account record...");
    List<SignalStorageRecord> records = accountManager.readStorageRecords(storageServiceKey, Collections.singletonList(accountId.get()));
    SignalStorageRecord record = records.size() > 0 ? records.get(0) : null;
    if (record == null) {
        Log.w(TAG, "Could not find account record, even though we had an ID! Not restoring.");
        return;
    }
    SignalAccountRecord accountRecord = record.getAccount().orNull();
    if (accountRecord == null) {
        Log.w(TAG, "The storage record didn't actually have an account on it! Not restoring.");
        return;
    }
    Log.i(TAG, "Applying changes locally...");
    SignalDatabase.getRawDatabase().beginTransaction();
    try {
        StorageSyncHelper.applyAccountStorageSyncUpdates(context, Recipient.self(), accountRecord, false);
        SignalDatabase.getRawDatabase().setTransactionSuccessful();
    } finally {
        SignalDatabase.getRawDatabase().endTransaction();
    }
    JobManager jobManager = ApplicationDependencies.getJobManager();
    if (accountRecord.getAvatarUrlPath().isPresent()) {
        Log.i(TAG, "Fetching avatar...");
        Optional<JobTracker.JobState> state = jobManager.runSynchronously(new RetrieveProfileAvatarJob(Recipient.self(), accountRecord.getAvatarUrlPath().get()), LIFESPAN / 2);
        if (state.isPresent()) {
            Log.i(TAG, "Avatar retrieved successfully. " + state.get());
        } else {
            Log.w(TAG, "Avatar retrieval did not complete in time (or otherwise failed).");
        }
    } else {
        Log.i(TAG, "No avatar present. Not fetching.");
    }
    Log.i(TAG, "Refreshing attributes...");
    Optional<JobTracker.JobState> state = jobManager.runSynchronously(new RefreshAttributesJob(), LIFESPAN / 2);
    if (state.isPresent()) {
        Log.i(TAG, "Attributes refreshed successfully. " + state.get());
    } else {
        Log.w(TAG, "Attribute refresh did not complete in time (or otherwise failed).");
    }
}
Also used : SignalAccountRecord(org.whispersystems.signalservice.api.storage.SignalAccountRecord) SignalStorageManifest(org.whispersystems.signalservice.api.storage.SignalStorageManifest) SignalServiceAccountManager(org.whispersystems.signalservice.api.SignalServiceAccountManager) SignalStorageRecord(org.whispersystems.signalservice.api.storage.SignalStorageRecord) JobManager(org.thoughtcrime.securesms.jobmanager.JobManager) StorageId(org.whispersystems.signalservice.api.storage.StorageId) StorageKey(org.whispersystems.signalservice.api.storage.StorageKey)

Example 8 with SignalStorageManifest

use of org.whispersystems.signalservice.api.storage.SignalStorageManifest in project Signal-Android by WhisperSystems.

the class StorageForcePushJob method onRun.

@Override
protected void onRun() throws IOException, RetryLaterException {
    if (SignalStore.account().isLinkedDevice()) {
        Log.i(TAG, "Only the primary device can force push");
        return;
    }
    StorageKey storageServiceKey = SignalStore.storageService().getOrCreateStorageKey();
    SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
    RecipientDatabase recipientDatabase = SignalDatabase.recipients();
    UnknownStorageIdDatabase storageIdDatabase = SignalDatabase.unknownStorageIds();
    long currentVersion = accountManager.getStorageManifestVersion();
    Map<RecipientId, StorageId> oldContactStorageIds = recipientDatabase.getContactStorageSyncIdsMap();
    long newVersion = currentVersion + 1;
    Map<RecipientId, StorageId> newContactStorageIds = generateContactStorageIds(oldContactStorageIds);
    List<SignalStorageRecord> inserts = Stream.of(oldContactStorageIds.keySet()).map(recipientDatabase::getRecordForSync).withoutNulls().map(s -> StorageSyncModels.localToRemoteRecord(s, Objects.requireNonNull(newContactStorageIds.get(s.getId())).getRaw())).toList();
    SignalStorageRecord accountRecord = StorageSyncHelper.buildAccountRecord(context, Recipient.self().fresh());
    List<StorageId> allNewStorageIds = new ArrayList<>(newContactStorageIds.values());
    inserts.add(accountRecord);
    allNewStorageIds.add(accountRecord.getId());
    SignalStorageManifest manifest = new SignalStorageManifest(newVersion, allNewStorageIds);
    StorageSyncValidations.validateForcePush(manifest, inserts, Recipient.self().fresh());
    try {
        if (newVersion > 1) {
            Log.i(TAG, String.format(Locale.ENGLISH, "Force-pushing data. Inserting %d IDs.", inserts.size()));
            if (accountManager.resetStorageRecords(storageServiceKey, manifest, inserts).isPresent()) {
                Log.w(TAG, "Hit a conflict. Trying again.");
                throw new RetryLaterException();
            }
        } else {
            Log.i(TAG, String.format(Locale.ENGLISH, "First version, normal push. Inserting %d IDs.", inserts.size()));
            if (accountManager.writeStorageRecords(storageServiceKey, manifest, inserts, Collections.emptyList()).isPresent()) {
                Log.w(TAG, "Hit a conflict. Trying again.");
                throw new RetryLaterException();
            }
        }
    } catch (InvalidKeyException e) {
        Log.w(TAG, "Hit an invalid key exception, which likely indicates a conflict.");
        throw new RetryLaterException(e);
    }
    Log.i(TAG, "Force push succeeded. Updating local manifest version to: " + newVersion);
    SignalStore.storageService().setManifest(manifest);
    recipientDatabase.applyStorageIdUpdates(newContactStorageIds);
    recipientDatabase.applyStorageIdUpdates(Collections.singletonMap(Recipient.self().getId(), accountRecord.getId()));
    storageIdDatabase.deleteAll();
}
Also used : SignalStore(org.thoughtcrime.securesms.keyvalue.SignalStore) SignalServiceAccountManager(org.whispersystems.signalservice.api.SignalServiceAccountManager) StorageSyncValidations(org.thoughtcrime.securesms.storage.StorageSyncValidations) SignalDatabase(org.thoughtcrime.securesms.database.SignalDatabase) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) Stream(com.annimon.stream.Stream) NonNull(androidx.annotation.NonNull) Data(org.thoughtcrime.securesms.jobmanager.Data) StorageSyncModels(org.thoughtcrime.securesms.storage.StorageSyncModels) HashMap(java.util.HashMap) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) InvalidKeyException(org.whispersystems.libsignal.InvalidKeyException) ArrayList(java.util.ArrayList) StorageKey(org.whispersystems.signalservice.api.storage.StorageKey) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) StorageSyncHelper(org.thoughtcrime.securesms.storage.StorageSyncHelper) Locale(java.util.Locale) Map(java.util.Map) Recipient(org.thoughtcrime.securesms.recipients.Recipient) SignalStorageManifest(org.whispersystems.signalservice.api.storage.SignalStorageManifest) StorageId(org.whispersystems.signalservice.api.storage.StorageId) PushNetworkException(org.whispersystems.signalservice.api.push.exceptions.PushNetworkException) ApplicationDependencies(org.thoughtcrime.securesms.dependencies.ApplicationDependencies) UnknownStorageIdDatabase(org.thoughtcrime.securesms.database.UnknownStorageIdDatabase) NetworkConstraint(org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint) IOException(java.io.IOException) Objects(java.util.Objects) TimeUnit(java.util.concurrent.TimeUnit) Log(org.signal.core.util.logging.Log) List(java.util.List) Job(org.thoughtcrime.securesms.jobmanager.Job) SignalStorageRecord(org.whispersystems.signalservice.api.storage.SignalStorageRecord) Collections(java.util.Collections) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) SignalStorageManifest(org.whispersystems.signalservice.api.storage.SignalStorageManifest) SignalServiceAccountManager(org.whispersystems.signalservice.api.SignalServiceAccountManager) ArrayList(java.util.ArrayList) SignalStorageRecord(org.whispersystems.signalservice.api.storage.SignalStorageRecord) InvalidKeyException(org.whispersystems.libsignal.InvalidKeyException) StorageId(org.whispersystems.signalservice.api.storage.StorageId) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) UnknownStorageIdDatabase(org.thoughtcrime.securesms.database.UnknownStorageIdDatabase) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) StorageKey(org.whispersystems.signalservice.api.storage.StorageKey)

Example 9 with SignalStorageManifest

use of org.whispersystems.signalservice.api.storage.SignalStorageManifest in project Signal-Android by WhisperSystems.

the class StorageSyncValidations method validateManifestAndInserts.

private static void validateManifestAndInserts(@NonNull SignalStorageManifest manifest, @NonNull List<SignalStorageRecord> inserts, @NonNull Recipient self) {
    Set<StorageId> allSet = new HashSet<>(manifest.getStorageIds());
    Set<StorageId> insertSet = new HashSet<>(Stream.of(inserts).map(SignalStorageRecord::getId).toList());
    Set<ByteBuffer> rawIdSet = Stream.of(allSet).map(id -> ByteBuffer.wrap(id.getRaw())).collect(Collectors.toSet());
    if (allSet.size() != manifest.getStorageIds().size()) {
        throw new DuplicateStorageIdError();
    }
    if (rawIdSet.size() != allSet.size()) {
        throw new DuplicateRawIdError();
    }
    if (inserts.size() > insertSet.size()) {
        throw new DuplicateInsertInWriteError();
    }
    int accountCount = 0;
    for (StorageId id : manifest.getStorageIds()) {
        accountCount += id.getType() == ManifestRecord.Identifier.Type.ACCOUNT_VALUE ? 1 : 0;
    }
    if (accountCount > 1) {
        throw new MultipleAccountError();
    }
    if (accountCount == 0) {
        throw new MissingAccountError();
    }
    for (SignalStorageRecord insert : inserts) {
        if (!allSet.contains(insert.getId())) {
            throw new InsertNotPresentInFullIdSetError();
        }
        if (insert.isUnknown()) {
            throw new UnknownInsertError();
        }
        if (insert.getContact().isPresent()) {
            SignalServiceAddress address = insert.getContact().get().getAddress();
            if (self.requireE164().equals(address.getNumber().or("")) || self.requireServiceId().equals(address.getServiceId())) {
                throw new SelfAddedAsContactError();
            }
        }
    }
}
Also used : Collectors(com.annimon.stream.Collectors) Stream(com.annimon.stream.Stream) NonNull(androidx.annotation.NonNull) Base64(org.thoughtcrime.securesms.util.Base64) ManifestRecord(org.whispersystems.signalservice.internal.storage.protos.ManifestRecord) Set(java.util.Set) SetUtil(org.thoughtcrime.securesms.util.SetUtil) SignalServiceAddress(org.whispersystems.signalservice.api.push.SignalServiceAddress) ByteBuffer(java.nio.ByteBuffer) HashSet(java.util.HashSet) Log(org.signal.core.util.logging.Log) List(java.util.List) Recipient(org.thoughtcrime.securesms.recipients.Recipient) SignalStorageRecord(org.whispersystems.signalservice.api.storage.SignalStorageRecord) SignalStorageManifest(org.whispersystems.signalservice.api.storage.SignalStorageManifest) StorageId(org.whispersystems.signalservice.api.storage.StorageId) SignalStorageRecord(org.whispersystems.signalservice.api.storage.SignalStorageRecord) StorageId(org.whispersystems.signalservice.api.storage.StorageId) ByteBuffer(java.nio.ByteBuffer) SignalServiceAddress(org.whispersystems.signalservice.api.push.SignalServiceAddress) HashSet(java.util.HashSet)

Example 10 with SignalStorageManifest

use of org.whispersystems.signalservice.api.storage.SignalStorageManifest in project Signal-Android by WhisperSystems.

the class SignalServiceAccountManager method writeStorageRecords.

/**
 * @return If there was a conflict, the latest {@link SignalStorageManifest}. Otherwise absent.
 */
private Optional<SignalStorageManifest> writeStorageRecords(StorageKey storageKey, SignalStorageManifest manifest, List<SignalStorageRecord> inserts, List<byte[]> deletes, boolean clearAll) throws IOException, InvalidKeyException {
    ManifestRecord.Builder manifestRecordBuilder = ManifestRecord.newBuilder().setVersion(manifest.getVersion());
    for (StorageId id : manifest.getStorageIds()) {
        ManifestRecord.Identifier idProto = ManifestRecord.Identifier.newBuilder().setRaw(ByteString.copyFrom(id.getRaw())).setType(ManifestRecord.Identifier.Type.forNumber(id.getType())).build();
        manifestRecordBuilder.addIdentifiers(idProto);
    }
    String authToken = this.pushServiceSocket.getStorageAuth();
    StorageManifestKey manifestKey = storageKey.deriveManifestKey(manifest.getVersion());
    byte[] encryptedRecord = SignalStorageCipher.encrypt(manifestKey, manifestRecordBuilder.build().toByteArray());
    StorageManifest storageManifest = StorageManifest.newBuilder().setVersion(manifest.getVersion()).setValue(ByteString.copyFrom(encryptedRecord)).build();
    WriteOperation.Builder writeBuilder = WriteOperation.newBuilder().setManifest(storageManifest);
    for (SignalStorageRecord insert : inserts) {
        writeBuilder.addInsertItem(SignalStorageModels.localToRemoteStorageRecord(insert, storageKey));
    }
    if (clearAll) {
        writeBuilder.setClearAll(true);
    } else {
        for (byte[] delete : deletes) {
            writeBuilder.addDeleteKey(ByteString.copyFrom(delete));
        }
    }
    Optional<StorageManifest> conflict = this.pushServiceSocket.writeStorageContacts(authToken, writeBuilder.build());
    if (conflict.isPresent()) {
        StorageManifestKey conflictKey = storageKey.deriveManifestKey(conflict.get().getVersion());
        byte[] rawManifestRecord = SignalStorageCipher.decrypt(conflictKey, conflict.get().getValue().toByteArray());
        ManifestRecord record = ManifestRecord.parseFrom(rawManifestRecord);
        List<StorageId> ids = new ArrayList<>(record.getIdentifiersCount());
        for (ManifestRecord.Identifier id : record.getIdentifiersList()) {
            ids.add(StorageId.forType(id.getRaw().toByteArray(), id.getType().getNumber()));
        }
        SignalStorageManifest conflictManifest = new SignalStorageManifest(record.getVersion(), ids);
        return Optional.of(conflictManifest);
    } else {
        return Optional.absent();
    }
}
Also used : SignalStorageManifest(org.whispersystems.signalservice.api.storage.SignalStorageManifest) ManifestRecord(org.whispersystems.signalservice.internal.storage.protos.ManifestRecord) StorageManifestKey(org.whispersystems.signalservice.api.storage.StorageManifestKey) ArrayList(java.util.ArrayList) SignalStorageRecord(org.whispersystems.signalservice.api.storage.SignalStorageRecord) ByteString(com.google.protobuf.ByteString) StorageId(org.whispersystems.signalservice.api.storage.StorageId) StorageManifest(org.whispersystems.signalservice.internal.storage.protos.StorageManifest) SignalStorageManifest(org.whispersystems.signalservice.api.storage.SignalStorageManifest) WriteOperation(org.whispersystems.signalservice.internal.storage.protos.WriteOperation)

Aggregations

SignalStorageManifest (org.whispersystems.signalservice.api.storage.SignalStorageManifest)14 SignalStorageRecord (org.whispersystems.signalservice.api.storage.SignalStorageRecord)10 StorageId (org.whispersystems.signalservice.api.storage.StorageId)10 ByteString (com.google.protobuf.ByteString)6 Recipient (org.thoughtcrime.securesms.recipients.Recipient)6 SignalServiceAccountManager (org.whispersystems.signalservice.api.SignalServiceAccountManager)6 StorageKey (org.whispersystems.signalservice.api.storage.StorageKey)6 StorageManifest (org.whispersystems.signalservice.internal.storage.protos.StorageManifest)6 NonNull (androidx.annotation.NonNull)4 Stream (com.annimon.stream.Stream)4 ArrayList (java.util.ArrayList)4 List (java.util.List)4 Log (org.signal.core.util.logging.Log)4 UnknownStorageIdDatabase (org.thoughtcrime.securesms.database.UnknownStorageIdDatabase)4 InvalidKeyException (org.whispersystems.libsignal.InvalidKeyException)4 RetryLaterException (org.thoughtcrime.securesms.transport.RetryLaterException)3 SignalAccountRecord (org.whispersystems.signalservice.api.storage.SignalAccountRecord)3 ManifestRecord (org.whispersystems.signalservice.internal.storage.protos.ManifestRecord)3 Collectors (com.annimon.stream.Collectors)2 IOException (java.io.IOException)2