Remove identity store cache.

fork-5.53.8
Greyson Parrelli 2021-08-27 10:39:36 -04:00 zatwierdzone przez Alex Hart
rodzic 7790cac0ee
commit a57adcb2b0
1 zmienionych plików z 67 dodań i 88 usunięć

Wyświetl plik

@ -3,40 +3,37 @@ package org.thoughtcrime.securesms.crypto.storage;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.database.model.IdentityStoreRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.LRUCache;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.state.IdentityKeyStore;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class TextSecureIdentityKeyStore implements IdentityKeyStore {
private static final String TAG = Log.tag(TextSecureIdentityKeyStore.class);
private static final int TIMESTAMP_THRESHOLD_SECONDS = 5;
private static final Object LOCK = new Object();
private static final int TIMESTAMP_THRESHOLD_SECONDS = 5;
private static final String TAG = Log.tag(TextSecureIdentityKeyStore.class);
private static final Object LOCK = new Object();
private final Context context;
private final Cache cache;
public TextSecureIdentityKeyStore(Context context) {
this.context = context;
this.cache = new Cache(context);
}
@Override
@ -49,44 +46,40 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
return TextSecurePreferences.getLocalRegistrationId(context);
}
@Override
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
return saveIdentity(address, identityKey, false) == SaveResult.UPDATE;
}
public @NonNull SaveResult saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) {
synchronized (LOCK) {
IdentityStoreRecord identityRecord = cache.get(address.getName());
RecipientId recipientId = RecipientId.fromExternalPush(address.getName());
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
RecipientId recipientId = RecipientId.fromExternalPush(address.getName());
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(recipientId);
if (identityRecord == null) {
if (!identityRecord.isPresent()) {
Log.i(TAG, "Saving new identity...");
cache.save(address.getName(), recipientId, identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval);
identityDatabase.saveIdentity(recipientId, identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval);
return SaveResult.NEW;
}
if (!identityRecord.getIdentityKey().equals(identityKey)) {
Log.i(TAG, "Replacing existing identity... Existing: " + identityRecord.getIdentityKey().hashCode() + " New: " + identityKey.hashCode());
if (!identityRecord.get().getIdentityKey().equals(identityKey)) {
Log.i(TAG, "Replacing existing identity... Existing: " + identityRecord.get().getIdentityKey().hashCode() + " New: " + identityKey.hashCode());
VerifiedStatus verifiedStatus;
if (identityRecord.getVerifiedStatus() == VerifiedStatus.VERIFIED ||
identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED)
if (identityRecord.get().getVerifiedStatus() == VerifiedStatus.VERIFIED ||
identityRecord.get().getVerifiedStatus() == VerifiedStatus.UNVERIFIED)
{
verifiedStatus = VerifiedStatus.UNVERIFIED;
} else {
verifiedStatus = VerifiedStatus.DEFAULT;
}
cache.save(address.getName(), recipientId, identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval);
identityDatabase.saveIdentity(recipientId, identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval);
IdentityUtil.markIdentityUpdate(context, recipientId);
SessionUtil.archiveSiblingSessions(address);
DatabaseFactory.getSenderKeySharedDatabase(context).deleteAllFor(recipientId);
return SaveResult.UPDATE;
}
if (isNonBlockingApprovalRequired(identityRecord)) {
if (isNonBlockingApprovalRequired(identityRecord.get())) {
Log.i(TAG, "Setting approval status...");
cache.setApproval(identityRecord, recipientId, nonBlockingApproval);
identityDatabase.setApproval(recipientId, nonBlockingApproval);
return SaveResult.NON_BLOCKING_APPROVAL_REQUIRED;
}
@ -94,50 +87,73 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
}
}
@Override
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
return saveIdentity(address, identityKey, false) == SaveResult.UPDATE;
}
@Override
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
boolean isSelf = address.getName().equals(TextSecurePreferences.getLocalUuid(context).toString()) ||
address.getName().equals(TextSecurePreferences.getLocalNumber(context));
synchronized (LOCK) {
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
RecipientId ourRecipientId = Recipient.self().getId();
RecipientId theirRecipientId = RecipientId.fromExternalPush(address.getName());
if (isSelf) {
return identityKey.equals(IdentityKeyUtil.getIdentityKey(context));
}
if (ourRecipientId.equals(theirRecipientId)) {
return identityKey.equals(IdentityKeyUtil.getIdentityKey(context));
}
IdentityStoreRecord record = cache.get(address.getName());
switch (direction) {
case SENDING:
return isTrustedForSending(identityKey, record);
case RECEIVING:
return true;
default:
throw new AssertionError("Unknown direction: " + direction);
switch (direction) {
case SENDING: return isTrustedForSending(identityKey, identityDatabase.getIdentity(theirRecipientId));
case RECEIVING: return true;
default: throw new AssertionError("Unknown direction: " + direction);
}
} else {
Log.w(TAG, "Tried to check if identity is trusted for " + address.getName() + ", but no matching recipient existed!");
switch (direction) {
case SENDING: return false;
case RECEIVING: return true;
default: throw new AssertionError("Unknown direction: " + direction);
}
}
}
}
@Override
public IdentityKey getIdentity(SignalProtocolAddress address) {
IdentityStoreRecord record = cache.get(address.getName());
return record != null ? record.getIdentityKey() : null;
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
RecipientId recipientId = RecipientId.fromExternalPush(address.getName());
Optional<IdentityRecord> record = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipientId);
if (record.isPresent()) {
return record.get().getIdentityKey();
} else {
return null;
}
} else {
Log.w(TAG, "Tried to get identity for " + address.getName() + ", but no matching recipient existed!");
return null;
}
}
private boolean isTrustedForSending(@NonNull IdentityKey identityKey, @Nullable IdentityStoreRecord identityRecord) {
if (identityRecord == null) {
private boolean isTrustedForSending(IdentityKey identityKey, Optional<IdentityRecord> identityRecord) {
if (!identityRecord.isPresent()) {
Log.w(TAG, "Nothing here, returning true...");
return true;
}
if (!identityKey.equals(identityRecord.getIdentityKey())) {
Log.w(TAG, "Identity keys don't match... service: " + identityKey.hashCode() + " database: " + identityRecord.getIdentityKey().hashCode());
if (!identityKey.equals(identityRecord.get().getIdentityKey())) {
Log.w(TAG, "Identity keys don't match... service: " + identityKey.hashCode() + " database: " + identityRecord.get().getIdentityKey().hashCode());
return false;
}
if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) {
if (identityRecord.get().getVerifiedStatus() == VerifiedStatus.UNVERIFIED) {
Log.w(TAG, "Needs unverified approval!");
return false;
}
if (isNonBlockingApprovalRequired(identityRecord)) {
if (isNonBlockingApprovalRequired(identityRecord.get())) {
Log.w(TAG, "Needs non-blocking approval!");
return false;
}
@ -145,47 +161,10 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
return true;
}
private boolean isNonBlockingApprovalRequired(IdentityStoreRecord record) {
return !record.getFirstUse() &&
!record.getNonblockingApproval() &&
System.currentTimeMillis() - record.getTimestamp() < TimeUnit.SECONDS.toMillis(TIMESTAMP_THRESHOLD_SECONDS);
}
private static final class Cache {
private final Context context;
private final Map<String, IdentityStoreRecord> cache;
Cache(@NonNull Context context) {
this.context = context;
this.cache = new LRUCache<>(200);
}
public synchronized @Nullable IdentityStoreRecord get(@NonNull String addressName) {
if (cache.containsKey(addressName)) {
return cache.get(addressName);
} else {
IdentityStoreRecord record = DatabaseFactory.getIdentityDatabase(context).getIdentityStoreRecord(addressName);
cache.put(addressName, record);
return record;
}
}
public synchronized void save(@NonNull String addressName, @NonNull RecipientId recipientId, @NonNull IdentityKey identityKey, @NonNull VerifiedStatus verifiedStatus, boolean firstUse, long timestamp, boolean nonBlockingApproval) {
DatabaseFactory.getIdentityDatabase(context).saveIdentity(addressName, recipientId, identityKey, verifiedStatus, firstUse, timestamp, nonBlockingApproval);
cache.put(addressName, new IdentityStoreRecord(addressName, identityKey, verifiedStatus, firstUse, timestamp, nonBlockingApproval));
}
public synchronized void setApproval(@NonNull IdentityStoreRecord record, @NonNull RecipientId recipientId, boolean nonblockingApproval) {
DatabaseFactory.getIdentityDatabase(context).setApproval(record.getAddressName(), recipientId, nonblockingApproval);
cache.put(record.getAddressName(),
new IdentityStoreRecord(record.getAddressName(),
record.getIdentityKey(),
record.getVerifiedStatus(),
record.getFirstUse(),
record.getTimestamp(),
nonblockingApproval));
}
private boolean isNonBlockingApprovalRequired(IdentityRecord identityRecord) {
return !identityRecord.isFirstUse() &&
System.currentTimeMillis() - identityRecord.getTimestamp() < TimeUnit.SECONDS.toMillis(TIMESTAMP_THRESHOLD_SECONDS) &&
!identityRecord.isApprovedNonBlocking();
}
public enum SaveResult {