mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-23 20:48:43 +00:00
Convert SvrValues to kotlin.
This commit is contained in:
@@ -111,7 +111,6 @@ import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWra
|
|||||||
|
|
||||||
import java.io.InterruptedIOException;
|
import java.io.InterruptedIOException;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -167,7 +166,7 @@ public class ApplicationContext extends Application implements AppForegroundObse
|
|||||||
.addBlocking("crash-handling", this::initializeCrashHandling)
|
.addBlocking("crash-handling", this::initializeCrashHandling)
|
||||||
.addBlocking("rx-init", this::initializeRx)
|
.addBlocking("rx-init", this::initializeRx)
|
||||||
.addBlocking("event-bus", () -> EventBus.builder().logNoSubscriberMessages(false).installDefaultEventBus())
|
.addBlocking("event-bus", () -> EventBus.builder().logNoSubscriberMessages(false).installDefaultEventBus())
|
||||||
.addBlocking("scrubber", () -> Scrubber.setIdentifierHmacKeyProvider(() -> SignalStore.svr().getOrCreateMasterKey().deriveLoggingKey()))
|
.addBlocking("scrubber", () -> Scrubber.setIdentifierHmacKeyProvider(() -> SignalStore.svr().getMasterKey().deriveLoggingKey()))
|
||||||
.addBlocking("first-launch", this::initializeFirstEverAppLaunch)
|
.addBlocking("first-launch", this::initializeFirstEverAppLaunch)
|
||||||
.addBlocking("app-migrations", this::initializeApplicationMigrations)
|
.addBlocking("app-migrations", this::initializeApplicationMigrations)
|
||||||
.addBlocking("lifecycle-observer", () -> AppForegroundObserver.addListener(this))
|
.addBlocking("lifecycle-observer", () -> AppForegroundObserver.addListener(this))
|
||||||
|
|||||||
@@ -717,7 +717,7 @@ object BackupRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun validate(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData): ValidationResult {
|
fun validate(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData): ValidationResult {
|
||||||
val masterKey = SignalStore.svr.getOrCreateMasterKey()
|
val masterKey = SignalStore.svr.masterKey
|
||||||
val key = LibSignalMessageBackupKey(masterKey.serialize(), Aci.parseFromBinary(selfData.aci.toByteArray()))
|
val key = LibSignalMessageBackupKey(masterKey.serialize(), Aci.parseFromBinary(selfData.aci.toByteArray()))
|
||||||
|
|
||||||
return MessageBackup.validate(key, MessageBackup.Purpose.REMOTE_BACKUP, inputStreamFactory, length)
|
return MessageBackup.validate(key, MessageBackup.Purpose.REMOTE_BACKUP, inputStreamFactory, length)
|
||||||
|
|||||||
@@ -507,7 +507,7 @@ class ChangeNumberViewModel : ViewModel() {
|
|||||||
val currentState = store.value
|
val currentState = store.value
|
||||||
val code = currentState.enteredCode ?: throw IllegalStateException("Can't construct registration data without entered code!")
|
val code = currentState.enteredCode ?: throw IllegalStateException("Can't construct registration data without entered code!")
|
||||||
val e164: String = number.e164Number ?: throw IllegalStateException("Can't construct registration data without E164!")
|
val e164: String = number.e164Number ?: throw IllegalStateException("Can't construct registration data without E164!")
|
||||||
val recoveryPassword = if (currentState.sessionId == null) SignalStore.svr.getRecoveryPassword() else null
|
val recoveryPassword = if (currentState.sessionId == null) SignalStore.svr.recoveryPassword else null
|
||||||
val fcmToken = RegistrationRepository.getFcmToken(context)
|
val fcmToken = RegistrationRepository.getFcmToken(context)
|
||||||
return RegistrationData(code, e164, password, RegistrationRepository.getRegistrationId(), RegistrationRepository.getProfileKey(e164), fcmToken, RegistrationRepository.getPniRegistrationId(), recoveryPassword)
|
return RegistrationData(code, e164, password, RegistrationRepository.getRegistrationId(), RegistrationRepository.getProfileKey(e164), fcmToken, RegistrationRepository.getPniRegistrationId(), recoveryPassword)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class InternalSvrPlaygroundViewModel : ViewModel() {
|
|||||||
disposables += Single
|
disposables += Single
|
||||||
.fromCallable {
|
.fromCallable {
|
||||||
_state.value.selected.toImplementation()
|
_state.value.selected.toImplementation()
|
||||||
.setPin(_state.value.userPin, SignalStore.svr.getOrCreateMasterKey())
|
.setPin(_state.value.userPin, SignalStore.svr.masterKey)
|
||||||
.execute()
|
.execute()
|
||||||
}
|
}
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.net.NotPushRegisteredException;
|
import org.thoughtcrime.securesms.net.NotPushRegisteredException;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
|
||||||
@@ -72,7 +71,7 @@ public class MultiDeviceKeysUpdateJob extends BaseJob {
|
|||||||
SignalServiceMessageSender messageSender = AppDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = AppDependencies.getSignalServiceMessageSender();
|
||||||
StorageKey storageServiceKey = SignalStore.storageService().getOrCreateStorageKey();
|
StorageKey storageServiceKey = SignalStore.storageService().getOrCreateStorageKey();
|
||||||
|
|
||||||
messageSender.sendSyncMessage(SignalServiceSyncMessage.forKeys(new KeysMessage(Optional.ofNullable(storageServiceKey), Optional.of(SignalStore.svr().getOrCreateMasterKey())))
|
messageSender.sendSyncMessage(SignalServiceSyncMessage.forKeys(new KeysMessage(Optional.ofNullable(storageServiceKey), Optional.of(SignalStore.svr().getMasterKey())))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class ResetSvrGuessCountJob private constructor(
|
|||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
val masterKey: MasterKey = SignalStore.svr.getOrCreateMasterKey()
|
val masterKey: MasterKey = SignalStore.svr.masterKey
|
||||||
|
|
||||||
val svr3Result = if (svr3Complete) {
|
val svr3Result = if (svr3Complete) {
|
||||||
Log.d(TAG, "Already reset guess count on SVR3. Skipping.")
|
Log.d(TAG, "Already reset guess count on SVR3. Skipping.")
|
||||||
@@ -138,7 +138,7 @@ class ResetSvrGuessCountJob private constructor(
|
|||||||
authTokenSaver: (AuthCredentials) -> Unit
|
authTokenSaver: (AuthCredentials) -> Unit
|
||||||
): Result {
|
): Result {
|
||||||
val session: PinChangeSession = if (serializedChangeSession != null) {
|
val session: PinChangeSession = if (serializedChangeSession != null) {
|
||||||
svr.resumePinChangeSession(pin, SignalStore.svr.getOrCreateMasterKey(), serializedChangeSession)
|
svr.resumePinChangeSession(pin, SignalStore.svr.masterKey, serializedChangeSession)
|
||||||
} else {
|
} else {
|
||||||
svr.setPin(pin, masterKey)
|
svr.setPin(pin, masterKey)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ class Svr2MirrorJob private constructor(parameters: Parameters, private var seri
|
|||||||
val svr2: SecureValueRecoveryV2 = AppDependencies.signalServiceAccountManager.getSecureValueRecoveryV2(BuildConfig.SVR2_MRENCLAVE)
|
val svr2: SecureValueRecoveryV2 = AppDependencies.signalServiceAccountManager.getSecureValueRecoveryV2(BuildConfig.SVR2_MRENCLAVE)
|
||||||
|
|
||||||
val session: PinChangeSession = serializedChangeSession?.let { session ->
|
val session: PinChangeSession = serializedChangeSession?.let { session ->
|
||||||
svr2.resumePinChangeSession(pin, SignalStore.svr.getOrCreateMasterKey(), session)
|
svr2.resumePinChangeSession(pin, SignalStore.svr.masterKey, session)
|
||||||
} ?: svr2.setPin(pin, SignalStore.svr.getOrCreateMasterKey())
|
} ?: svr2.setPin(pin, SignalStore.svr.masterKey)
|
||||||
|
|
||||||
serializedChangeSession = session.serialize()
|
serializedChangeSession = session.serialize()
|
||||||
|
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ class Svr3MirrorJob private constructor(parameters: Parameters, private var seri
|
|||||||
val svr3: SecureValueRecoveryV3 = AppDependencies.signalServiceAccountManager.getSecureValueRecoveryV3(AppDependencies.libsignalNetwork)
|
val svr3: SecureValueRecoveryV3 = AppDependencies.signalServiceAccountManager.getSecureValueRecoveryV3(AppDependencies.libsignalNetwork)
|
||||||
|
|
||||||
val session: PinChangeSession = serializedChangeSession?.let { session ->
|
val session: PinChangeSession = serializedChangeSession?.let { session ->
|
||||||
svr3.resumePinChangeSession(pin, SignalStore.svr.getOrCreateMasterKey(), session)
|
svr3.resumePinChangeSession(pin, SignalStore.svr.masterKey, session)
|
||||||
} ?: svr3.setPin(pin, SignalStore.svr.getOrCreateMasterKey())
|
} ?: svr3.setPin(pin, SignalStore.svr.masterKey)
|
||||||
|
|
||||||
serializedChangeSession = session.serialize()
|
serializedChangeSession = session.serialize()
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
|
|||||||
* Key used to backup messages.
|
* Key used to backup messages.
|
||||||
*/
|
*/
|
||||||
val messageBackupKey: MessageBackupKey
|
val messageBackupKey: MessageBackupKey
|
||||||
get() = SignalStore.svr.getOrCreateMasterKey().derivateMessageBackupKey()
|
get() = SignalStore.svr.masterKey.derivateMessageBackupKey()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key used to backup media. Purely random and separate from the message backup key.
|
* Key used to backup media. Purely random and separate from the message backup key.
|
||||||
@@ -108,14 +108,14 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
|
|||||||
|
|
||||||
Log.i(TAG, "Generating MediaRootBackupKey...", Throwable())
|
Log.i(TAG, "Generating MediaRootBackupKey...", Throwable())
|
||||||
val bytes = Util.getSecretBytes(32)
|
val bytes = Util.getSecretBytes(32)
|
||||||
putBlob(KEY_MEDIA_ROOT_BACKUP_KEY, bytes)
|
store.beginWrite().putBlob(KEY_MEDIA_ROOT_BACKUP_KEY, bytes).commit()
|
||||||
return MediaRootBackupKey(bytes)
|
return MediaRootBackupKey(bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
lock.withLock {
|
lock.withLock {
|
||||||
Log.i(TAG, "Setting MediaRootBackupKey", Throwable())
|
Log.i(TAG, "Setting MediaRootBackupKey", Throwable())
|
||||||
putBlob(KEY_MEDIA_ROOT_BACKUP_KEY, value.value)
|
store.beginWrite().putBlob(KEY_MEDIA_ROOT_BACKUP_KEY, value.value).commit()
|
||||||
mediaCredentials.clearAll()
|
mediaCredentials.clearAll()
|
||||||
cachedMediaCdnPath = null
|
cachedMediaCdnPath = null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class StorageServiceValues extends SignalStoreValues {
|
|||||||
if (getStore().containsKey(SYNC_STORAGE_KEY)) {
|
if (getStore().containsKey(SYNC_STORAGE_KEY)) {
|
||||||
return new StorageKey(getBlob(SYNC_STORAGE_KEY, null));
|
return new StorageKey(getBlob(SYNC_STORAGE_KEY, null));
|
||||||
}
|
}
|
||||||
return SignalStore.svr().getOrCreateMasterKey().deriveStorageServiceKey();
|
return SignalStore.svr().getMasterKey().deriveStorageServiceKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getLastSyncTime() {
|
public long getLastSyncTime() {
|
||||||
|
|||||||
@@ -1,278 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.keyvalue;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.signal.core.util.StringStringSerializer;
|
|
||||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
|
||||||
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
|
||||||
import org.whispersystems.signalservice.api.kbs.PinHashUtil;
|
|
||||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public final class SvrValues extends SignalStoreValues {
|
|
||||||
|
|
||||||
public static final String REGISTRATION_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 PIN = "kbs.pin";
|
|
||||||
private static final String LOCK_LOCAL_PIN_HASH = "kbs.registration_lock_local_pin_hash";
|
|
||||||
private static final String LAST_CREATE_FAILED_TIMESTAMP = "kbs.last_create_failed_timestamp";
|
|
||||||
public static final String OPTED_OUT = "kbs.opted_out";
|
|
||||||
private static final String PIN_FORGOTTEN_OR_SKIPPED = "kbs.pin.forgotten.or.skipped";
|
|
||||||
private static final String SVR2_AUTH_TOKENS = "kbs.kbs_auth_tokens";
|
|
||||||
private static final String SVR_LAST_AUTH_REFRESH_TIMESTAMP = "kbs.kbs_auth_tokens.last_refresh_timestamp";
|
|
||||||
private static final String SVR3_AUTH_TOKENS = "kbs.svr3_auth_tokens";
|
|
||||||
|
|
||||||
SvrValues(KeyValueStore store) {
|
|
||||||
super(store);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void onFirstEverAppLaunch() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@NonNull
|
|
||||||
List<String> getKeysToIncludeInBackup() {
|
|
||||||
return List.of(
|
|
||||||
SVR2_AUTH_TOKENS,
|
|
||||||
SVR3_AUTH_TOKENS
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deliberately does not clear the {@link #MASTER_KEY}.
|
|
||||||
*/
|
|
||||||
public void clearRegistrationLockAndPin() {
|
|
||||||
getStore().beginWrite()
|
|
||||||
.remove(REGISTRATION_LOCK_ENABLED)
|
|
||||||
.remove(TOKEN_RESPONSE)
|
|
||||||
.remove(LOCK_LOCAL_PIN_HASH)
|
|
||||||
.remove(PIN)
|
|
||||||
.remove(LAST_CREATE_FAILED_TIMESTAMP)
|
|
||||||
.remove(OPTED_OUT)
|
|
||||||
.remove(SVR2_AUTH_TOKENS)
|
|
||||||
.remove(SVR_LAST_AUTH_REFRESH_TIMESTAMP)
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void setMasterKey(@NonNull MasterKey masterKey, @NonNull String pin) {
|
|
||||||
getStore().beginWrite()
|
|
||||||
.putBlob(MASTER_KEY, masterKey.serialize())
|
|
||||||
.putString(LOCK_LOCAL_PIN_HASH, PinHashUtil.localPinHash(pin))
|
|
||||||
.putString(PIN, pin)
|
|
||||||
.putLong(LAST_CREATE_FAILED_TIMESTAMP, -1)
|
|
||||||
.putBoolean(OPTED_OUT, false)
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void setPinIfNotPresent(@NonNull String pin) {
|
|
||||||
if (getStore().getString(PIN, null) == null) {
|
|
||||||
getStore().beginWrite().putString(PIN, pin).commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void setRegistrationLockEnabled(boolean enabled) {
|
|
||||||
putBoolean(REGISTRATION_LOCK_ENABLED, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not registration lock V2 is enabled.
|
|
||||||
*/
|
|
||||||
public synchronized boolean isRegistrationLockEnabled() {
|
|
||||||
return getBoolean(REGISTRATION_LOCK_ENABLED, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void onPinCreateFailure() {
|
|
||||||
putLong(LAST_CREATE_FAILED_TIMESTAMP, System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the last time the user attempted to create a PIN, it failed.
|
|
||||||
*/
|
|
||||||
public synchronized boolean lastPinCreateFailed() {
|
|
||||||
return getLong(LAST_CREATE_FAILED_TIMESTAMP, -1) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds or creates the master key. Therefore this will always return a master key whether backed
|
|
||||||
* up or not.
|
|
||||||
* <p>
|
|
||||||
* If you only want a key when it's backed up, use {@link #getPinBackedMasterKey()}.
|
|
||||||
*/
|
|
||||||
public synchronized @NonNull MasterKey getOrCreateMasterKey() {
|
|
||||||
byte[] blob = getStore().getBlob(MASTER_KEY, null);
|
|
||||||
|
|
||||||
if (blob == null) {
|
|
||||||
getStore().beginWrite()
|
|
||||||
.putBlob(MASTER_KEY, MasterKey.createNew(new SecureRandom()).serialize())
|
|
||||||
.commit();
|
|
||||||
blob = getBlob(MASTER_KEY, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MasterKey(blob);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns null if master key is not backed up by a pin.
|
|
||||||
*/
|
|
||||||
public synchronized @Nullable MasterKey getPinBackedMasterKey() {
|
|
||||||
if (!isRegistrationLockEnabled()) return null;
|
|
||||||
return getMasterKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized @Nullable MasterKey getMasterKey() {
|
|
||||||
byte[] blob = getBlob(MASTER_KEY, null);
|
|
||||||
return blob != null ? new MasterKey(blob) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable String getRegistrationLockToken() {
|
|
||||||
MasterKey masterKey = getPinBackedMasterKey();
|
|
||||||
if (masterKey == null) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return masterKey.deriveRegistrationLock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized @Nullable String getRecoveryPassword() {
|
|
||||||
MasterKey masterKey = getMasterKey();
|
|
||||||
if (masterKey != null && hasPin()) {
|
|
||||||
return masterKey.deriveRegistrationRecoveryPassword();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized @Nullable String getPin() {
|
|
||||||
return getString(PIN, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized @Nullable String getLocalPinHash() {
|
|
||||||
return getString(LOCK_LOCAL_PIN_HASH, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean hasPin() {
|
|
||||||
return getLocalPinHash() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean isPinForgottenOrSkipped() {
|
|
||||||
return getBoolean(PIN_FORGOTTEN_OR_SKIPPED, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void setPinForgottenOrSkipped(boolean value) {
|
|
||||||
putBoolean(PIN_FORGOTTEN_OR_SKIPPED, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void putSvr2AuthTokens(List<String> tokens) {
|
|
||||||
putList(SVR2_AUTH_TOKENS, tokens, StringStringSerializer.INSTANCE);
|
|
||||||
setLastRefreshAuthTimestamp(System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void putSvr3AuthTokens(List<String> tokens) {
|
|
||||||
putList(SVR3_AUTH_TOKENS, tokens, StringStringSerializer.INSTANCE);
|
|
||||||
setLastRefreshAuthTimestamp(System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized List<String> getSvr2AuthTokens() {
|
|
||||||
return getList(SVR2_AUTH_TOKENS, StringStringSerializer.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized List<String> getSvr3AuthTokens() {
|
|
||||||
return getList(SVR3_AUTH_TOKENS, StringStringSerializer.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps the 10 most recent KBS auth tokens.
|
|
||||||
* @param token
|
|
||||||
* @return whether the token was added (new) or ignored (already existed)
|
|
||||||
*/
|
|
||||||
public synchronized boolean appendSvr2AuthTokenToList(String token) {
|
|
||||||
List<String> tokens = getSvr2AuthTokens();
|
|
||||||
if (tokens.contains(token)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
final List<String> result = Stream.concat(Stream.of(token), tokens.stream()).limit(10).collect(Collectors.toList());
|
|
||||||
putSvr2AuthTokens(result);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps the 10 most recent SVR3 auth tokens.
|
|
||||||
* @param token
|
|
||||||
* @return whether the token was added (new) or ignored (already existed)
|
|
||||||
*/
|
|
||||||
public synchronized boolean appendSvr3AuthTokenToList(String token) {
|
|
||||||
List<String> tokens = getSvr3AuthTokens();
|
|
||||||
if (tokens.contains(token)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
final List<String> result = Stream.concat(Stream.of(token), tokens.stream()).limit(10).collect(Collectors.toList());
|
|
||||||
putSvr3AuthTokens(result);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removeSvr2AuthTokens(@NonNull List<String> invalid) {
|
|
||||||
List<String> tokens = new ArrayList<>(getSvr2AuthTokens());
|
|
||||||
if (tokens.removeAll(invalid)) {
|
|
||||||
putSvr2AuthTokens(tokens);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removeSvr3AuthTokens(@NonNull List<String> invalid) {
|
|
||||||
List<String> tokens = new ArrayList<>(getSvr3AuthTokens());
|
|
||||||
if (tokens.removeAll(invalid)) {
|
|
||||||
putSvr3AuthTokens(tokens);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void optOut() {
|
|
||||||
getStore().beginWrite()
|
|
||||||
.putBoolean(OPTED_OUT, true)
|
|
||||||
.remove(TOKEN_RESPONSE)
|
|
||||||
.putBlob(MASTER_KEY, MasterKey.createNew(new SecureRandom()).serialize())
|
|
||||||
.remove(LOCK_LOCAL_PIN_HASH)
|
|
||||||
.remove(PIN)
|
|
||||||
.putLong(LAST_CREATE_FAILED_TIMESTAMP, -1)
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean hasOptedOut() {
|
|
||||||
return getBoolean(OPTED_OUT, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized @Nullable TokenResponse getRegistrationLockTokenResponse() {
|
|
||||||
String token = getStore().getString(TOKEN_RESPONSE, null);
|
|
||||||
|
|
||||||
if (token == null) return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
return JsonUtils.fromJson(token, TokenResponse.class);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setLastRefreshAuthTimestamp(long timestamp) {
|
|
||||||
putLong(SVR_LAST_AUTH_REFRESH_TIMESTAMP, timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLastRefreshAuthTimestamp() {
|
|
||||||
return getLong(SVR_LAST_AUTH_REFRESH_TIMESTAMP, 0L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
package org.thoughtcrime.securesms.keyvalue
|
||||||
|
|
||||||
|
import org.signal.core.util.StringStringSerializer
|
||||||
|
import org.signal.core.util.logging.Log
|
||||||
|
import org.thoughtcrime.securesms.util.JsonUtils
|
||||||
|
import org.whispersystems.signalservice.api.kbs.MasterKey
|
||||||
|
import org.whispersystems.signalservice.api.kbs.PinHashUtil.localPinHash
|
||||||
|
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse
|
||||||
|
import java.io.IOException
|
||||||
|
import java.security.SecureRandom
|
||||||
|
|
||||||
|
class SvrValues internal constructor(store: KeyValueStore) : SignalStoreValues(store) {
|
||||||
|
companion object {
|
||||||
|
private val TAG = Log.tag(SvrValues::class)
|
||||||
|
|
||||||
|
const val REGISTRATION_LOCK_ENABLED: String = "kbs.v2_lock_enabled"
|
||||||
|
const val OPTED_OUT: String = "kbs.opted_out"
|
||||||
|
|
||||||
|
private const val MASTER_KEY = "kbs.registration_lock_master_key"
|
||||||
|
private const val TOKEN_RESPONSE = "kbs.token_response"
|
||||||
|
private const val PIN = "kbs.pin"
|
||||||
|
private const val LOCK_LOCAL_PIN_HASH = "kbs.registration_lock_local_pin_hash"
|
||||||
|
private const val LAST_CREATE_FAILED_TIMESTAMP = "kbs.last_create_failed_timestamp"
|
||||||
|
private const val PIN_FORGOTTEN_OR_SKIPPED = "kbs.pin.forgotten.or.skipped"
|
||||||
|
private const val SVR2_AUTH_TOKENS = "kbs.kbs_auth_tokens"
|
||||||
|
private const val SVR_LAST_AUTH_REFRESH_TIMESTAMP = "kbs.kbs_auth_tokens.last_refresh_timestamp"
|
||||||
|
private const val SVR3_AUTH_TOKENS = "kbs.svr3_auth_tokens"
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun onFirstEverAppLaunch() = Unit
|
||||||
|
|
||||||
|
public override fun getKeysToIncludeInBackup(): List<String> {
|
||||||
|
return listOf(
|
||||||
|
SVR2_AUTH_TOKENS,
|
||||||
|
SVR3_AUTH_TOKENS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deliberately does not clear the [MASTER_KEY]. */
|
||||||
|
@Synchronized
|
||||||
|
fun clearRegistrationLockAndPin() {
|
||||||
|
store.beginWrite()
|
||||||
|
.remove(REGISTRATION_LOCK_ENABLED)
|
||||||
|
.remove(TOKEN_RESPONSE)
|
||||||
|
.remove(LOCK_LOCAL_PIN_HASH)
|
||||||
|
.remove(PIN)
|
||||||
|
.remove(LAST_CREATE_FAILED_TIMESTAMP)
|
||||||
|
.remove(OPTED_OUT)
|
||||||
|
.remove(SVR2_AUTH_TOKENS)
|
||||||
|
.remove(SVR_LAST_AUTH_REFRESH_TIMESTAMP)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun setMasterKey(masterKey: MasterKey, pin: String) {
|
||||||
|
store.beginWrite()
|
||||||
|
.putBlob(MASTER_KEY, masterKey.serialize())
|
||||||
|
.putString(LOCK_LOCAL_PIN_HASH, localPinHash(pin))
|
||||||
|
.putString(PIN, pin)
|
||||||
|
.putLong(LAST_CREATE_FAILED_TIMESTAMP, -1)
|
||||||
|
.putBoolean(OPTED_OUT, false)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun setPinIfNotPresent(pin: String) {
|
||||||
|
if (store.getString(PIN, null) == null) {
|
||||||
|
store.beginWrite().putString(PIN, pin).commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether or not registration lock V2 is enabled. */
|
||||||
|
@get:Synchronized
|
||||||
|
@set:Synchronized
|
||||||
|
var isRegistrationLockEnabled: Boolean by booleanValue(REGISTRATION_LOCK_ENABLED, false)
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun onPinCreateFailure() {
|
||||||
|
putLong(LAST_CREATE_FAILED_TIMESTAMP, System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether or not the last time the user attempted to create a PIN, it failed. */
|
||||||
|
@Synchronized
|
||||||
|
fun lastPinCreateFailed(): Boolean {
|
||||||
|
return getLong(LAST_CREATE_FAILED_TIMESTAMP, -1) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val masterKey: MasterKey
|
||||||
|
/** Returns the Master Key, lazily creating one if needed. */
|
||||||
|
get() {
|
||||||
|
val blob = store.getBlob(MASTER_KEY, null)
|
||||||
|
if (blob != null) {
|
||||||
|
return MasterKey(blob)
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "Generating Master Key...", Throwable())
|
||||||
|
val masterKey = MasterKey.createNew(SecureRandom())
|
||||||
|
store.beginWrite().putBlob(MASTER_KEY, masterKey.serialize()).commit()
|
||||||
|
return masterKey
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val pinBackedMasterKey: MasterKey?
|
||||||
|
/** Returns null if master key is not backed up by a pin. */
|
||||||
|
get() {
|
||||||
|
if (!isRegistrationLockEnabled) return null
|
||||||
|
return rawMasterKey
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
private val rawMasterKey: MasterKey?
|
||||||
|
get() = getBlob(MASTER_KEY, null)?.let { MasterKey(it) }
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val registrationLockToken: String?
|
||||||
|
get() {
|
||||||
|
val masterKey = pinBackedMasterKey
|
||||||
|
return masterKey?.deriveRegistrationLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val recoveryPassword: String?
|
||||||
|
get() {
|
||||||
|
val masterKey = rawMasterKey
|
||||||
|
return if (masterKey != null && hasPin()) {
|
||||||
|
masterKey.deriveRegistrationRecoveryPassword()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val pin: String? by stringValue(PIN, null)
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val localPinHash: String? by stringValue(LOCK_LOCAL_PIN_HASH, null)
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun hasPin(): Boolean {
|
||||||
|
return localPinHash != null
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
@set:Synchronized
|
||||||
|
var isPinForgottenOrSkipped: Boolean by booleanValue(PIN_FORGOTTEN_OR_SKIPPED, false)
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun putSvr2AuthTokens(tokens: List<String>) {
|
||||||
|
putList(SVR2_AUTH_TOKENS, tokens, StringStringSerializer)
|
||||||
|
lastRefreshAuthTimestamp = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun putSvr3AuthTokens(tokens: List<String>) {
|
||||||
|
putList(SVR3_AUTH_TOKENS, tokens, StringStringSerializer)
|
||||||
|
lastRefreshAuthTimestamp = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val svr2AuthTokens: List<String>
|
||||||
|
get() = getList(SVR2_AUTH_TOKENS, StringStringSerializer).requireNoNulls()
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val svr3AuthTokens: List<String>
|
||||||
|
get() = getList(SVR3_AUTH_TOKENS, StringStringSerializer).requireNoNulls()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps the 10 most recent KBS auth tokens.
|
||||||
|
* @param token
|
||||||
|
* @return whether the token was added (new) or ignored (already existed)
|
||||||
|
*/
|
||||||
|
@Synchronized
|
||||||
|
fun appendSvr2AuthTokenToList(token: String): Boolean {
|
||||||
|
val tokens = svr2AuthTokens
|
||||||
|
if (tokens.contains(token)) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
val result = (listOf(token) + tokens).take(10)
|
||||||
|
putSvr2AuthTokens(result)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps the 10 most recent SVR3 auth tokens.
|
||||||
|
* @param token
|
||||||
|
* @return whether the token was added (new) or ignored (already existed)
|
||||||
|
*/
|
||||||
|
@Synchronized
|
||||||
|
fun appendSvr3AuthTokenToList(token: String): Boolean {
|
||||||
|
val tokens = svr3AuthTokens
|
||||||
|
if (tokens.contains(token)) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
val result = (listOf(token) + tokens).take(10)
|
||||||
|
putSvr3AuthTokens(result)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun removeSvr2AuthTokens(invalid: List<String>): Boolean {
|
||||||
|
val tokens: MutableList<String> = ArrayList(svr2AuthTokens)
|
||||||
|
if (tokens.removeAll(invalid)) {
|
||||||
|
putSvr2AuthTokens(tokens)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun removeSvr3AuthTokens(invalid: List<String>): Boolean {
|
||||||
|
val tokens: MutableList<String> = ArrayList(svr3AuthTokens)
|
||||||
|
if (tokens.removeAll(invalid)) {
|
||||||
|
putSvr3AuthTokens(tokens)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun optOut() {
|
||||||
|
store.beginWrite()
|
||||||
|
.putBoolean(OPTED_OUT, true)
|
||||||
|
.remove(TOKEN_RESPONSE)
|
||||||
|
.putBlob(MASTER_KEY, MasterKey.createNew(SecureRandom()).serialize())
|
||||||
|
.remove(LOCK_LOCAL_PIN_HASH)
|
||||||
|
.remove(PIN)
|
||||||
|
.putLong(LAST_CREATE_FAILED_TIMESTAMP, -1)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun hasOptedOut(): Boolean {
|
||||||
|
return getBoolean(OPTED_OUT, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val registrationLockTokenResponse: TokenResponse?
|
||||||
|
get() {
|
||||||
|
val token = store.getString(TOKEN_RESPONSE, null) ?: return null
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JsonUtils.fromJson(token, TokenResponse::class.java)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw AssertionError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastRefreshAuthTimestamp: Long by longValue(SVR_LAST_AUTH_REFRESH_TIMESTAMP, 0L)
|
||||||
|
}
|
||||||
@@ -154,7 +154,7 @@ object LinkDeviceRepository {
|
|||||||
aciIdentityKeyPair = SignalStore.account.aciIdentityKey,
|
aciIdentityKeyPair = SignalStore.account.aciIdentityKey,
|
||||||
pniIdentityKeyPair = SignalStore.account.pniIdentityKey,
|
pniIdentityKeyPair = SignalStore.account.pniIdentityKey,
|
||||||
profileKey = ProfileKeyUtil.getSelfProfileKey(),
|
profileKey = ProfileKeyUtil.getSelfProfileKey(),
|
||||||
masterKey = SignalStore.svr.getOrCreateMasterKey(),
|
masterKey = SignalStore.svr.masterKey,
|
||||||
code = verificationCodeResult.verificationCode,
|
code = verificationCodeResult.verificationCode,
|
||||||
ephemeralMessageBackupKey = ephemeralMessageBackupKey
|
ephemeralMessageBackupKey = ephemeralMessageBackupKey
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ object SvrRepository {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setPin(userPin: String, keyboardType: PinKeyboardType): BackupResponse {
|
fun setPin(userPin: String, keyboardType: PinKeyboardType): BackupResponse {
|
||||||
return operationLock.withLock {
|
return operationLock.withLock {
|
||||||
val masterKey: MasterKey = SignalStore.svr.getOrCreateMasterKey()
|
val masterKey: MasterKey = SignalStore.svr.masterKey
|
||||||
|
|
||||||
val writeTargets = writeImplementations
|
val writeTargets = writeImplementations
|
||||||
|
|
||||||
@@ -367,7 +367,7 @@ object SvrRepository {
|
|||||||
check(SignalStore.svr.hasPin() && !SignalStore.svr.hasOptedOut()) { "Must have a PIN to set a registration lock!" }
|
check(SignalStore.svr.hasPin() && !SignalStore.svr.hasOptedOut()) { "Must have a PIN to set a registration lock!" }
|
||||||
|
|
||||||
Log.i(TAG, "[enableRegistrationLockForUserWithPin] Enabling registration lock.", true)
|
Log.i(TAG, "[enableRegistrationLockForUserWithPin] Enabling registration lock.", true)
|
||||||
AppDependencies.signalServiceAccountManager.enableRegistrationLock(SignalStore.svr.getOrCreateMasterKey())
|
AppDependencies.signalServiceAccountManager.enableRegistrationLock(SignalStore.svr.masterKey)
|
||||||
SignalStore.svr.isRegistrationLockEnabled = true
|
SignalStore.svr.isRegistrationLockEnabled = true
|
||||||
Log.i(TAG, "[enableRegistrationLockForUserWithPin] Registration lock successfully enabled.", true)
|
Log.i(TAG, "[enableRegistrationLockForUserWithPin] Registration lock successfully enabled.", true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ data class RegistrationState(
|
|||||||
val phoneNumber: Phonenumber.PhoneNumber? = fetchExistingE164FromValues(),
|
val phoneNumber: Phonenumber.PhoneNumber? = fetchExistingE164FromValues(),
|
||||||
val inProgress: Boolean = false,
|
val inProgress: Boolean = false,
|
||||||
val isReRegister: Boolean = false,
|
val isReRegister: Boolean = false,
|
||||||
val recoveryPassword: String? = SignalStore.svr.getRecoveryPassword(),
|
val recoveryPassword: String? = SignalStore.svr.recoveryPassword,
|
||||||
val canSkipSms: Boolean = false,
|
val canSkipSms: Boolean = false,
|
||||||
val svr2AuthCredentials: AuthCredentials? = null,
|
val svr2AuthCredentials: AuthCredentials? = null,
|
||||||
val svr3AuthCredentials: Svr3Credentials? = null,
|
val svr3AuthCredentials: Svr3Credentials? = null,
|
||||||
|
|||||||
@@ -619,7 +619,7 @@ class RegistrationViewModel : ViewModel() {
|
|||||||
if (RegistrationRepository.doesPinMatchLocalHash(pin)) {
|
if (RegistrationRepository.doesPinMatchLocalHash(pin)) {
|
||||||
Log.d(TAG, "Found recovery password, attempting to re-register.")
|
Log.d(TAG, "Found recovery password, attempting to re-register.")
|
||||||
viewModelScope.launch(context = coroutineExceptionHandler) {
|
viewModelScope.launch(context = coroutineExceptionHandler) {
|
||||||
verifyReRegisterInternal(context, pin, SignalStore.svr.getOrCreateMasterKey())
|
verifyReRegisterInternal(context, pin, SignalStore.svr.masterKey)
|
||||||
setInProgress(false)
|
setInProgress(false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -789,7 +789,7 @@ class RegistrationViewModel : ViewModel() {
|
|||||||
reglock = true
|
reglock = true
|
||||||
if (pin == null && SignalStore.svr.registrationLockToken != null) {
|
if (pin == null && SignalStore.svr.registrationLockToken != null) {
|
||||||
Log.d(TAG, "Retrying registration with stored credentials.")
|
Log.d(TAG, "Retrying registration with stored credentials.")
|
||||||
result = RegistrationRepository.registerAccount(context, sessionId, registrationData, SignalStore.svr.pin) { SignalStore.svr.getOrCreateMasterKey() }
|
result = RegistrationRepository.registerAccount(context, sessionId, registrationData, SignalStore.svr.pin) { SignalStore.svr.masterKey }
|
||||||
} else if (result.svr2Credentials != null || result.svr3Credentials != null) {
|
} else if (result.svr2Credentials != null || result.svr3Credentials != null) {
|
||||||
Log.d(TAG, "Retrying registration with received credentials (svr2: ${result.svr2Credentials != null}, svr3: ${result.svr3Credentials != null}).")
|
Log.d(TAG, "Retrying registration with received credentials (svr2: ${result.svr2Credentials != null}, svr3: ${result.svr3Credentials != null}).")
|
||||||
val svr2Credentials = result.svr2Credentials
|
val svr2Credentials = result.svr2Credentials
|
||||||
@@ -894,7 +894,7 @@ class RegistrationViewModel : ViewModel() {
|
|||||||
val currentState = store.value
|
val currentState = store.value
|
||||||
val code = currentState.enteredCode
|
val code = currentState.enteredCode
|
||||||
val e164: String = currentState.phoneNumber?.toE164() ?: throw IllegalStateException("Can't construct registration data without E164!")
|
val e164: String = currentState.phoneNumber?.toE164() ?: throw IllegalStateException("Can't construct registration data without E164!")
|
||||||
val recoveryPassword = if (currentState.sessionId == null) SignalStore.svr.getRecoveryPassword() else null
|
val recoveryPassword = if (currentState.sessionId == null) SignalStore.svr.recoveryPassword else null
|
||||||
return RegistrationData(code, e164, password, RegistrationRepository.getRegistrationId(), RegistrationRepository.getProfileKey(e164), currentState.fcmToken, RegistrationRepository.getPniRegistrationId(), recoveryPassword)
|
return RegistrationData(code, e164, password, RegistrationRepository.getRegistrationId(), RegistrationRepository.getProfileKey(e164), currentState.fcmToken, RegistrationRepository.getPniRegistrationId(), recoveryPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user