Signal-Android/src/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore....

153 lines
5.7 KiB
Java

package org.thoughtcrime.securesms.crypto.storage;
import android.content.Context;
import org.thoughtcrime.securesms.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.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.IdentityUtil;
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.concurrent.TimeUnit;
public class TextSecureIdentityKeyStore implements IdentityKeyStore {
private static final int TIMESTAMP_THRESHOLD_SECONDS = 5;
private static final String TAG = TextSecureIdentityKeyStore.class.getSimpleName();
private static final Object LOCK = new Object();
private final Context context;
public TextSecureIdentityKeyStore(Context context) {
this.context = context;
}
@Override
public IdentityKeyPair getIdentityKeyPair() {
return IdentityKeyUtil.getIdentityKeyPair(context);
}
@Override
public int getLocalRegistrationId() {
return TextSecurePreferences.getLocalRegistrationId(context);
}
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) {
synchronized (LOCK) {
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
Recipient recipient = Recipient.external(context, address.getName());
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(recipient.getId());
if (!identityRecord.isPresent()) {
Log.i(TAG, "Saving new identity...");
identityDatabase.saveIdentity(recipient.getId(), identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval);
return false;
}
if (!identityRecord.get().getIdentityKey().equals(identityKey)) {
Log.i(TAG, "Replacing existing identity...");
VerifiedStatus verifiedStatus;
if (identityRecord.get().getVerifiedStatus() == VerifiedStatus.VERIFIED ||
identityRecord.get().getVerifiedStatus() == VerifiedStatus.UNVERIFIED)
{
verifiedStatus = VerifiedStatus.UNVERIFIED;
} else {
verifiedStatus = VerifiedStatus.DEFAULT;
}
identityDatabase.saveIdentity(recipient.getId(), identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval);
IdentityUtil.markIdentityUpdate(context, recipient);
SessionUtil.archiveSiblingSessions(context, address);
return true;
}
if (isNonBlockingApprovalRequired(identityRecord.get())) {
Log.i(TAG, "Setting approval status...");
identityDatabase.setApproval(recipient.getId(), nonBlockingApproval);
return false;
}
return false;
}
}
@Override
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
return saveIdentity(address, identityKey, false);
}
@Override
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
synchronized (LOCK) {
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
RecipientId ourRecipientId = Recipient.self().getId();
RecipientId theirRecipientId = Recipient.external(context, address.getName()).getId();
if (ourRecipientId.equals(theirRecipientId)) {
return identityKey.equals(IdentityKeyUtil.getIdentityKey(context));
}
switch (direction) {
case SENDING: return isTrustedForSending(identityKey, identityDatabase.getIdentity(theirRecipientId));
case RECEIVING: return true;
default: throw new AssertionError("Unknown direction: " + direction);
}
}
}
@Override
public IdentityKey getIdentity(SignalProtocolAddress address) {
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
Optional<IdentityRecord> record = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipientId);
if (record.isPresent()) {
return record.get().getIdentityKey();
} else {
return 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.get().getIdentityKey())) {
Log.w(TAG, "Identity keys don't match...");
return false;
}
if (identityRecord.get().getVerifiedStatus() == VerifiedStatus.UNVERIFIED) {
Log.w(TAG, "Needs unverified approval!");
return false;
}
if (isNonBlockingApprovalRequired(identityRecord.get())) {
Log.w(TAG, "Needs non-blocking approval!");
return false;
}
return true;
}
private boolean isNonBlockingApprovalRequired(IdentityRecord identityRecord) {
return !identityRecord.isFirstUse() &&
System.currentTimeMillis() - identityRecord.getTimestamp() < TimeUnit.SECONDS.toMillis(TIMESTAMP_THRESHOLD_SECONDS) &&
!identityRecord.isApprovedNonBlocking();
}
}