Separate PINs from Registration Lock.

You can now have a PIN without having registration lock.

Note: We still need to change the registration flow to allow non-reglock
users to enter their PIN.
This commit is contained in:
Greyson Parrelli
2020-04-02 17:09:25 -04:00
parent 3c6a7b76ca
commit 8e13403cca
48 changed files with 905 additions and 523 deletions

View File

@@ -3,8 +3,10 @@ package org.thoughtcrime.securesms.keyvalue;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.whispersystems.signalservice.api.RegistrationLockData;
import org.whispersystems.signalservice.api.KbsPinData;
import org.whispersystems.signalservice.api.kbs.MasterKey;
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
@@ -13,7 +15,7 @@ import java.security.SecureRandom;
public final class KbsValues {
private static final String V2_LOCK_ENABLED = "kbs.v2_lock_enabled";
public static final String V2_LOCK_ENABLED = "kbs.v2_lock_enabled";
private static final String MASTER_KEY = "kbs.registration_lock_master_key";
private static final String TOKEN_RESPONSE = "kbs.token_response";
private static final String LOCK_LOCAL_PIN_HASH = "kbs.registration_lock_local_pin_hash";
@@ -27,7 +29,7 @@ public final class KbsValues {
/**
* Deliberately does not clear the {@link #MASTER_KEY}.
*/
public void clearRegistrationLock() {
public void clearRegistrationLockAndPin() {
store.beginWrite()
.remove(V2_LOCK_ENABLED)
.remove(TOKEN_RESPONSE)
@@ -35,23 +37,30 @@ public final class KbsValues {
.commit();
}
public synchronized void setRegistrationLockMasterKey(@NonNull RegistrationLockData registrationLockData, @NonNull String localPinHash) {
MasterKey masterKey = registrationLockData.getMasterKey();
public synchronized void setKbsMasterKey(@NonNull KbsPinData pinData, @NonNull String localPinHash) {
MasterKey masterKey = pinData.getMasterKey();
String tokenResponse;
try {
tokenResponse = JsonUtils.toJson(registrationLockData.getTokenResponse());
tokenResponse = JsonUtils.toJson(pinData.getTokenResponse());
} catch (IOException e) {
throw new AssertionError(e);
}
store.beginWrite()
.putBoolean(V2_LOCK_ENABLED, true)
.putString(TOKEN_RESPONSE, tokenResponse)
.putBlob(MASTER_KEY, masterKey.serialize())
.putString(LOCK_LOCAL_PIN_HASH, localPinHash)
.commit();
}
public synchronized void setV2RegistrationLockEnabled(boolean enabled) {
store.beginWrite().putBoolean(V2_LOCK_ENABLED, enabled).apply();
}
public synchronized boolean isV2RegistrationLockEnabled() {
return store.getBoolean(V2_LOCK_ENABLED, false);
}
/**
* Finds or creates the master key. Therefore this will always return a master key whether backed
* up or not.
@@ -97,8 +106,8 @@ public final class KbsValues {
return store.getString(LOCK_LOCAL_PIN_HASH, null);
}
public synchronized boolean isV2RegistrationLockEnabled() {
return store.getBoolean(V2_LOCK_ENABLED, false);
public synchronized boolean hasPin() {
return getLocalPinHash() != null;
}
public synchronized @Nullable TokenResponse getRegistrationLockTokenResponse() {

View File

@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.keyvalue;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.lock.SignalPinReminders;
@@ -19,6 +20,7 @@ public final class PinValues {
private static final String LAST_SUCCESSFUL_ENTRY = "pin.last_successful_entry";
private static final String NEXT_INTERVAL = "pin.interval_index";
private static final String KEYBOARD_TYPE = "kbs.keyboard_type";
private static final String PIN_STATE = "pin.pin_state";
private final KeyValueStore store;
@@ -55,9 +57,9 @@ public final class PinValues {
.apply();
}
public void onPinChange() {
public void resetPinReminders() {
long nextInterval = SignalPinReminders.INITIAL_INTERVAL;
Log.i(TAG, "onPinChange() nextInterval: " + nextInterval);
Log.i(TAG, "resetPinReminders() nextInterval: " + nextInterval, new Throwable());
store.beginWrite()
.putLong(NEXT_INTERVAL, nextInterval)
@@ -82,4 +84,12 @@ public final class PinValues {
public @NonNull PinKeyboardType getKeyboardType() {
return PinKeyboardType.fromCode(store.getString(KEYBOARD_TYPE, null));
}
public void setPinState(@NonNull String pinState) {
store.beginWrite().putString(PIN_STATE, pinState).commit();
}
public @Nullable String getPinState() {
return store.getString(PIN_STATE, null);
}
}

View File

@@ -0,0 +1,70 @@
package org.thoughtcrime.securesms.keyvalue;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceDataStore;
import java.util.Set;
/**
* An implementation of the {@link PreferenceDataStore} interface to let us link preference screens
* to the {@link SignalStore}.
*/
public class SignalPreferenceDataStore extends PreferenceDataStore {
private final KeyValueStore store;
SignalPreferenceDataStore(@NonNull KeyValueStore store) {
this.store = store;
}
@Override
public void putString(String key, @Nullable String value) {
store.beginWrite().putString(key, value).apply();
}
@Override
public void putInt(String key, int value) {
store.beginWrite().putInteger(key, value).apply();
}
@Override
public void putLong(String key, long value) {
store.beginWrite().putLong(key, value).apply();
}
@Override
public void putFloat(String key, float value) {
store.beginWrite().putFloat(key, value).apply();
}
@Override
public void putBoolean(String key, boolean value) {
store.beginWrite().putBoolean(key, value).apply();
}
@Override
public @Nullable String getString(String key, @Nullable String defValue) {
return store.getString(key, defValue);
}
@Override
public int getInt(String key, int defValue) {
return store.getInteger(key, defValue);
}
@Override
public long getLong(String key, long defValue) {
return store.getLong(key, defValue);
}
@Override
public float getFloat(String key, float defValue) {
return store.getFloat(key, defValue);
}
@Override
public boolean getBoolean(String key, boolean defValue) {
return store.getBoolean(key, defValue);
}
}

View File

@@ -1,10 +1,10 @@
package org.thoughtcrime.securesms.keyvalue;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceDataStore;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.logging.SignalUncaughtExceptionHandler;
import org.thoughtcrime.securesms.util.FeatureFlags;
/**
* Simple, encrypted key-value store.
@@ -56,6 +56,10 @@ public final class SignalStore {
putLong(MESSAGE_REQUEST_ENABLE_TIME, time);
}
public static @NonNull PreferenceDataStore getPreferenceDataStore() {
return new SignalPreferenceDataStore(getStore());
}
/**
* Ensures any pending writes are finished. Only intended to be called by
* {@link SignalUncaughtExceptionHandler}.

View File

@@ -4,13 +4,13 @@ import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.whispersystems.signalservice.api.kbs.MasterKey;
import org.whispersystems.signalservice.api.storage.StorageKey;
import java.security.SecureRandom;
public class StorageServiceValues {
private static final String STORAGE_MASTER_KEY = "storage.storage_master_key";
private static final String LAST_SYNC_TIME = "storage.last_sync_time";
private static final String LAST_SYNC_TIME = "storage.last_sync_time";
private final KeyValueStore store;
@@ -18,23 +18,8 @@ public class StorageServiceValues {
this.store = store;
}
public synchronized MasterKey getOrCreateStorageMasterKey() {
byte[] blob = store.getBlob(STORAGE_MASTER_KEY, null);
if (blob == null) {
store.beginWrite()
.putBlob(STORAGE_MASTER_KEY, MasterKey.createNew(new SecureRandom()).serialize())
.commit();
blob = store.getBlob(STORAGE_MASTER_KEY, null);
}
return new MasterKey(blob);
}
public synchronized void rotateStorageMasterKey() {
store.beginWrite()
.putBlob(STORAGE_MASTER_KEY, MasterKey.createNew(new SecureRandom()).serialize())
.commit();
public synchronized StorageKey getOrCreateStorageKey() {
return SignalStore.kbsValues().getOrCreateMasterKey().deriveStorageServiceKey();
}
public long getLastSyncTime() {