mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-25 05:27:42 +00:00
Create account in single network request.
This commit is contained in:
@@ -36,7 +36,6 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
|
||||
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
@@ -88,6 +87,7 @@ class SignalActivityRule(private val othersCount: Int = 4) : ExternalResource()
|
||||
password = Util.getSecret(18),
|
||||
registrationId = registrationRepository.registrationId,
|
||||
profileKey = registrationRepository.getProfileKey("+15555550101"),
|
||||
preKeyCollections = RegistrationRepository.generatePreKeys()!!,
|
||||
fcmToken = null,
|
||||
pniRegistrationId = registrationRepository.pniRegistrationId,
|
||||
recoveryPassword = "asdfasdfasdfasdf"
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.signal.libsignal.protocol.util.Medium;
|
||||
import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountDataStore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -49,45 +50,73 @@ public class PreKeyUtil {
|
||||
private static final long ARCHIVE_AGE = TimeUnit.DAYS.toMillis(30);
|
||||
|
||||
public synchronized static @NonNull List<PreKeyRecord> generateAndStoreOneTimeEcPreKeys(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore) {
|
||||
int preKeyIdOffset = metadataStore.getNextEcOneTimePreKeyId();
|
||||
final List<PreKeyRecord> records = generateOneTimeEcPreKeys(preKeyIdOffset);
|
||||
|
||||
storeOneTimeEcPreKeys(protocolStore, metadataStore, preKeyIdOffset, records);
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
public synchronized static List<PreKeyRecord> generateOneTimeEcPreKeys(int preKeyIdOffset) {
|
||||
Log.i(TAG, "Generating one-time EC prekeys...");
|
||||
|
||||
List<PreKeyRecord> records = new LinkedList<>();
|
||||
int preKeyIdOffset = metadataStore.getNextEcOneTimePreKeyId();
|
||||
List<PreKeyRecord> records = new ArrayList<>(BATCH_SIZE);
|
||||
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
int preKeyId = (preKeyIdOffset + i) % Medium.MAX_VALUE;
|
||||
ECKeyPair keyPair = Curve.generateKeyPair();
|
||||
PreKeyRecord record = new PreKeyRecord(preKeyId, keyPair);
|
||||
|
||||
protocolStore.storePreKey(preKeyId, record);
|
||||
records.add(record);
|
||||
}
|
||||
|
||||
metadataStore.setNextEcOneTimePreKeyId((preKeyIdOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE);
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
public synchronized static @NonNull List<KyberPreKeyRecord> generateAndStoreOneTimeKyberPreKeys(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore) {
|
||||
Log.i(TAG, "Generating one-time kyber prekeys...");
|
||||
public synchronized static void storeOneTimeEcPreKeys(@NonNull SignalProtocolStore protocolStore, PreKeyMetadataStore metadataStore, int preKeyIdOffset, List<PreKeyRecord> prekeys) {
|
||||
Log.i(TAG, "Storing one-time EC prekeys...");
|
||||
|
||||
List<KyberPreKeyRecord> records = new LinkedList<>();
|
||||
int preKeyIdOffset = metadataStore.getNextKyberPreKeyId();
|
||||
|
||||
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
int preKeyId = (preKeyIdOffset + i) % Medium.MAX_VALUE;
|
||||
KyberPreKeyRecord record = generateKyberPreKey(preKeyId, protocolStore.getIdentityKeyPair().getPrivateKey());
|
||||
|
||||
protocolStore.storeKyberPreKey(preKeyId, record);
|
||||
records.add(record);
|
||||
for (PreKeyRecord record : prekeys) {
|
||||
protocolStore.storePreKey(record.getId(), record);
|
||||
}
|
||||
metadataStore.setNextEcOneTimePreKeyId((preKeyIdOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE);
|
||||
|
||||
metadataStore.setNextKyberPreKeyId((preKeyIdOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE);
|
||||
}
|
||||
|
||||
public synchronized static @NonNull List<KyberPreKeyRecord> generateAndStoreOneTimeKyberPreKeys(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore) {
|
||||
int preKeyIdOffset = metadataStore.getNextKyberPreKeyId();
|
||||
List<KyberPreKeyRecord> records = generateOneTimeKyberPreKeyRecords(preKeyIdOffset, protocolStore.getIdentityKeyPair().getPrivateKey());
|
||||
|
||||
storeOneTimeKyberPreKeys(protocolStore, metadataStore, preKeyIdOffset, records);
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static List<KyberPreKeyRecord> generateOneTimeKyberPreKeyRecords(int preKeyIdOffset, @NonNull ECPrivateKey privateKey) {
|
||||
Log.i(TAG, "Generating one-time kyber prekeys...");
|
||||
|
||||
List<KyberPreKeyRecord> records = new LinkedList<>();
|
||||
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
int preKeyId = (preKeyIdOffset + i) % Medium.MAX_VALUE;
|
||||
KyberPreKeyRecord record = generateKyberPreKey(preKeyId, privateKey);
|
||||
|
||||
records.add(record);
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
public synchronized static void storeOneTimeKyberPreKeys(@NonNull SignalProtocolStore protocolStore, PreKeyMetadataStore metadataStore, int preKeyIdOffset, List<KyberPreKeyRecord> prekeys) {
|
||||
Log.i(TAG, "Storing one-time kyber prekeys...");
|
||||
|
||||
for (KyberPreKeyRecord record : prekeys) {
|
||||
protocolStore.storeKyberPreKey(record.getId(), record);
|
||||
}
|
||||
metadataStore.setNextKyberPreKeyId((preKeyIdOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE);
|
||||
}
|
||||
|
||||
public synchronized static @NonNull SignedPreKeyRecord generateAndStoreSignedPreKey(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore) {
|
||||
return generateAndStoreSignedPreKey(protocolStore, metadataStore, protocolStore.getIdentityKeyPair().getPrivateKey());
|
||||
}
|
||||
@@ -96,18 +125,16 @@ public class PreKeyUtil {
|
||||
@NonNull PreKeyMetadataStore metadataStore,
|
||||
@NonNull ECPrivateKey privateKey)
|
||||
{
|
||||
Log.i(TAG, "Generating signed prekeys...");
|
||||
|
||||
int signedPreKeyId = metadataStore.getNextSignedPreKeyId();
|
||||
SignedPreKeyRecord record = generateSignedPreKey(signedPreKeyId, privateKey);
|
||||
|
||||
protocolStore.storeSignedPreKey(signedPreKeyId, record);
|
||||
metadataStore.setNextSignedPreKeyId((signedPreKeyId + 1) % Medium.MAX_VALUE);
|
||||
storeSignedPreKey(protocolStore, metadataStore, signedPreKeyId, record);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
public synchronized static @NonNull SignedPreKeyRecord generateSignedPreKey(int signedPreKeyId, @NonNull ECPrivateKey privateKey) {
|
||||
Log.i(TAG, "Generating signed prekeys...");
|
||||
|
||||
try {
|
||||
ECKeyPair keyPair = Curve.generateKeyPair();
|
||||
byte[] signature = Curve.calculateSignature(privateKey, keyPair.getPublicKey().serialize());
|
||||
@@ -118,6 +145,13 @@ public class PreKeyUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static void storeSignedPreKey(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore, int signedPreKeyId, SignedPreKeyRecord record) {
|
||||
Log.i(TAG, "Storing signed prekeys...");
|
||||
|
||||
protocolStore.storeSignedPreKey(signedPreKeyId, record);
|
||||
metadataStore.setNextSignedPreKeyId((signedPreKeyId + 1) % Medium.MAX_VALUE);
|
||||
}
|
||||
|
||||
public synchronized static @NonNull KyberPreKeyRecord generateAndStoreLastResortKyberPreKey(@NonNull SignalServiceAccountDataStore protocolStore, @NonNull PreKeyMetadataStore metadataStore) {
|
||||
return generateAndStoreLastResortKyberPreKey(protocolStore, metadataStore, protocolStore.getIdentityKeyPair().getPrivateKey());
|
||||
}
|
||||
@@ -128,20 +162,26 @@ public class PreKeyUtil {
|
||||
{
|
||||
int id = metadataStore.getNextKyberPreKeyId();
|
||||
KyberPreKeyRecord record = generateKyberPreKey(id, privateKey);
|
||||
|
||||
protocolStore.storeKyberPreKey(id, record);
|
||||
metadataStore.setNextKyberPreKeyId((id + 1) % Medium.MAX_VALUE);
|
||||
storeLastResortKyberPreKey(protocolStore, metadataStore, id, record);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
public synchronized static @NonNull KyberPreKeyRecord generateKyberPreKey(int id, @NonNull ECPrivateKey privateKey) {
|
||||
Log.i(TAG, "Generating kyber prekeys...");
|
||||
|
||||
KEMKeyPair keyPair = KEMKeyPair.generate(KEMKeyType.KYBER_1024);
|
||||
byte[] signature = privateKey.calculateSignature(keyPair.getPublicKey().serialize());
|
||||
|
||||
return new KyberPreKeyRecord(id, System.currentTimeMillis(), keyPair, signature);
|
||||
}
|
||||
|
||||
public synchronized static void storeLastResortKyberPreKey(@NonNull SignalServiceAccountDataStore protocolStore, @NonNull PreKeyMetadataStore metadataStore, int id, KyberPreKeyRecord record) {
|
||||
Log.i(TAG, "Storing kyber prekeys...");
|
||||
protocolStore.storeKyberPreKey(id, record);
|
||||
metadataStore.setNextKyberPreKeyId((id + 1) % Medium.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds all of the signed prekeys that are older than the archive age, and archive all but the youngest of those.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.registration
|
||||
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey
|
||||
import org.whispersystems.signalservice.api.account.PreKeyCollections
|
||||
|
||||
data class RegistrationData(
|
||||
val code: String,
|
||||
@@ -8,6 +9,7 @@ data class RegistrationData(
|
||||
val password: String,
|
||||
val registrationId: Int,
|
||||
val profileKey: ProfileKey,
|
||||
val preKeyCollections: PreKeyCollections,
|
||||
val fcmToken: String?,
|
||||
val pniRegistrationId: Int,
|
||||
val recoveryPassword: String?
|
||||
|
||||
@@ -9,12 +9,13 @@ import androidx.annotation.WorkerThread;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair;
|
||||
import org.signal.libsignal.protocol.state.KyberPreKeyRecord;
|
||||
import org.signal.libsignal.protocol.state.PreKeyRecord;
|
||||
import org.signal.libsignal.protocol.state.SignalProtocolStore;
|
||||
import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
|
||||
import org.signal.libsignal.protocol.util.KeyHelper;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.SenderKeyUtil;
|
||||
@@ -37,8 +38,9 @@ import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
|
||||
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.api.KbsPinData;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountDataStore;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.account.PreKeyCollection;
|
||||
import org.whispersystems.signalservice.api.account.PreKeyCollections;
|
||||
import org.whispersystems.signalservice.api.account.PreKeyUpload;
|
||||
import org.whispersystems.signalservice.api.push.ACI;
|
||||
import org.whispersystems.signalservice.api.push.PNI;
|
||||
@@ -145,16 +147,17 @@ public final class RegistrationRepository {
|
||||
ApplicationDependencies.getProtocolStore().pni().sessions().archiveAllSessions();
|
||||
SenderKeyUtil.clearAllState();
|
||||
|
||||
SignalServiceAccountManager accountManager = AccountManagerFactory.getInstance().createAuthenticated(context, aci, pni, registrationData.getE164(), SignalServiceAddress.DEFAULT_DEVICE_ID, registrationData.getPassword());
|
||||
SignalServiceAccountDataStoreImpl aciProtocolStore = ApplicationDependencies.getProtocolStore().aci();
|
||||
SignalServiceAccountDataStoreImpl pniProtocolStore = ApplicationDependencies.getProtocolStore().pni();
|
||||
SignalServiceAccountDataStoreImpl aciProtocolStore = ApplicationDependencies.getProtocolStore().aci();
|
||||
PreKeyCollection aciPreKeyCollection = registrationData.getPreKeyCollections().getAciPreKeyCollection();
|
||||
PreKeyMetadataStore aciMetadataStore = SignalStore.account().aciPreKeys();
|
||||
|
||||
generateAndRegisterPreKeys(ServiceIdType.ACI, accountManager, aciProtocolStore, SignalStore.account().aciPreKeys());
|
||||
generateAndRegisterPreKeys(ServiceIdType.PNI, accountManager, pniProtocolStore, SignalStore.account().pniPreKeys());
|
||||
SignalServiceAccountDataStoreImpl pniProtocolStore = ApplicationDependencies.getProtocolStore().pni();
|
||||
PreKeyCollection pniPreKeyCollection = registrationData.getPreKeyCollections().getPniPreKeyCollection();
|
||||
PreKeyMetadataStore pniMetadataStore = SignalStore.account().pniPreKeys();
|
||||
|
||||
storePreKeys(aciProtocolStore, aciMetadataStore, aciPreKeyCollection);
|
||||
storePreKeys(pniProtocolStore, pniMetadataStore, pniPreKeyCollection);
|
||||
|
||||
if (registrationData.isFcm()) {
|
||||
accountManager.setGcmId(Optional.ofNullable(registrationData.getFcmToken()));
|
||||
}
|
||||
|
||||
RecipientTable recipientTable = SignalDatabase.recipients();
|
||||
RecipientId selfId = Recipient.trustedPush(aci, pni, registrationData.getE164()).getId();
|
||||
@@ -186,24 +189,57 @@ public final class RegistrationRepository {
|
||||
ApplicationDependencies.getIncomingMessageObserver();
|
||||
}
|
||||
|
||||
private void generateAndRegisterPreKeys(@NonNull ServiceIdType serviceIdType,
|
||||
@NonNull SignalServiceAccountManager accountManager,
|
||||
@NonNull SignalServiceAccountDataStore protocolStore,
|
||||
@NonNull PreKeyMetadataStore metadataStore)
|
||||
throws IOException
|
||||
{
|
||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore);
|
||||
List<PreKeyRecord> oneTimeEcPreKeys = PreKeyUtil.generateAndStoreOneTimeEcPreKeys(protocolStore, metadataStore);
|
||||
KyberPreKeyRecord lastResortKyberPreKey = PreKeyUtil.generateAndStoreLastResortKyberPreKey(protocolStore, metadataStore);
|
||||
List<KyberPreKeyRecord> oneTimeKyberPreKeys = PreKeyUtil.generateAndStoreOneTimeKyberPreKeys(protocolStore, metadataStore);
|
||||
public static @Nullable PreKeyCollections generatePreKeys() {
|
||||
final IdentityKeyPair keyPair = IdentityKeyUtil.generateIdentityKeyPair();
|
||||
final PreKeyMetadataStore aciMetadataStore = SignalStore.account().aciPreKeys();
|
||||
final PreKeyMetadataStore pniMetadataStore = SignalStore.account().pniPreKeys();
|
||||
|
||||
accountManager.setPreKeys(new PreKeyUpload(serviceIdType,
|
||||
protocolStore.getIdentityKeyPair().getPublicKey(),
|
||||
signedPreKey,
|
||||
oneTimeEcPreKeys,
|
||||
lastResortKyberPreKey,
|
||||
oneTimeKyberPreKeys));
|
||||
try {
|
||||
return new PreKeyCollections(keyPair,
|
||||
generatePreKeysForType(ServiceIdType.ACI, keyPair, aciMetadataStore),
|
||||
generatePreKeysForType(ServiceIdType.PNI, keyPair, pniMetadataStore)
|
||||
);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to generate prekeys!", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static PreKeyCollection generatePreKeysForType(ServiceIdType serviceIdType, IdentityKeyPair keyPair, PreKeyMetadataStore metadataStore) throws IOException {
|
||||
int nextSignedPreKeyId = metadataStore.getNextSignedPreKeyId();
|
||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(nextSignedPreKeyId, keyPair.getPrivateKey());
|
||||
metadataStore.setActiveSignedPreKeyId(signedPreKey.getId());
|
||||
|
||||
int ecOneTimePreKeyIdOffset = metadataStore.getNextEcOneTimePreKeyId();
|
||||
List<PreKeyRecord> oneTimeEcPreKeys = PreKeyUtil.generateOneTimeEcPreKeys(ecOneTimePreKeyIdOffset);
|
||||
|
||||
|
||||
int nextKyberPreKeyId = metadataStore.getNextKyberPreKeyId();
|
||||
KyberPreKeyRecord lastResortKyberPreKey = PreKeyUtil.generateKyberPreKey(nextKyberPreKeyId, keyPair.getPrivateKey());
|
||||
metadataStore.setLastResortKyberPreKeyId(nextKyberPreKeyId);
|
||||
|
||||
int oneTimeKyberPreKeyIdOffset = metadataStore.getNextKyberPreKeyId();
|
||||
List<KyberPreKeyRecord> oneTimeKyberPreKeys = PreKeyUtil.generateOneTimeKyberPreKeyRecords(oneTimeKyberPreKeyIdOffset, keyPair.getPrivateKey());
|
||||
|
||||
return new PreKeyCollection(
|
||||
nextSignedPreKeyId,
|
||||
ecOneTimePreKeyIdOffset,
|
||||
nextKyberPreKeyId,
|
||||
oneTimeKyberPreKeyIdOffset,
|
||||
serviceIdType,
|
||||
keyPair.getPublicKey(),
|
||||
signedPreKey,
|
||||
oneTimeEcPreKeys,
|
||||
lastResortKyberPreKey,
|
||||
oneTimeKyberPreKeys
|
||||
);
|
||||
}
|
||||
|
||||
private static void storePreKeys(SignalServiceAccountDataStoreImpl protocolStore, PreKeyMetadataStore metadataStore, PreKeyCollection preKeyCollection) {
|
||||
PreKeyUtil.storeSignedPreKey(protocolStore, metadataStore, preKeyCollection.getNextSignedPreKeyId(), preKeyCollection.getSignedPreKey());
|
||||
PreKeyUtil.storeOneTimeEcPreKeys(protocolStore, metadataStore, preKeyCollection.getEcOneTimePreKeyIdOffset(), preKeyCollection.getOneTimeEcPreKeys());
|
||||
PreKeyUtil.storeLastResortKyberPreKey(protocolStore, metadataStore, preKeyCollection.getLastResortKyberPreKeyId(), preKeyCollection.getLastResortKyberPreKey());
|
||||
PreKeyUtil.storeOneTimeKyberPreKeys(protocolStore, metadataStore, preKeyCollection.getOneTimeKyberPreKeyIdOffset(), preKeyCollection.getOneTimeKyberPreKeys());
|
||||
metadataStore.setSignedPreKeyRegistered(true);
|
||||
}
|
||||
|
||||
@@ -239,4 +275,5 @@ public final class RegistrationRepository {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ class VerifyAccountRepository(private val context: Application) {
|
||||
)
|
||||
|
||||
return Single.fromCallable {
|
||||
val response = accountManager.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, true)
|
||||
val response = accountManager.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, registrationData.preKeyCollections, registrationData.fcmToken, true)
|
||||
VerifyResponse.from(response, kbsData, pin)
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
@@ -235,6 +235,7 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
getRegistrationSecret(),
|
||||
registrationRepository.getRegistrationId(),
|
||||
registrationRepository.getProfileKey(getNumber().getE164Number()),
|
||||
Objects.requireNonNull(RegistrationRepository.generatePreKeys()),
|
||||
getFcmToken(),
|
||||
registrationRepository.getPniRegistrationId(),
|
||||
getSessionId() != null ? null : getRecoveryPassword());
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||
import org.whispersystems.signalservice.api.account.AccountAttributes;
|
||||
import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest;
|
||||
import org.whispersystems.signalservice.api.account.PniKeyDistributionRequest;
|
||||
import org.whispersystems.signalservice.api.account.PreKeyCollections;
|
||||
import org.whispersystems.signalservice.api.account.PreKeyUpload;
|
||||
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
|
||||
import org.whispersystems.signalservice.api.crypto.ProfileCipherOutputStream;
|
||||
@@ -316,9 +317,9 @@ public class SignalServiceAccountManager {
|
||||
}
|
||||
}
|
||||
|
||||
public @Nonnull ServiceResponse<VerifyAccountResponse> registerAccount(@Nullable String sessionId, @Nullable String recoveryPassword, AccountAttributes attributes, boolean skipDeviceTransfer) {
|
||||
public @Nonnull ServiceResponse<VerifyAccountResponse> registerAccount(@Nullable String sessionId, @Nullable String recoveryPassword, AccountAttributes attributes, PreKeyCollections preKeys, String fcmToken, boolean skipDeviceTransfer) {
|
||||
try {
|
||||
VerifyAccountResponse response = pushServiceSocket.submitRegistrationRequest(sessionId, recoveryPassword, attributes, skipDeviceTransfer);
|
||||
VerifyAccountResponse response = pushServiceSocket.submitRegistrationRequest(sessionId, recoveryPassword, attributes, preKeys, fcmToken, skipDeviceTransfer);
|
||||
return ServiceResponse.forResult(response, 200, null);
|
||||
} catch (IOException e) {
|
||||
return ServiceResponse.forUnknownError(e);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.account
|
||||
|
||||
import org.signal.libsignal.protocol.IdentityKey
|
||||
import org.signal.libsignal.protocol.state.KyberPreKeyRecord
|
||||
import org.signal.libsignal.protocol.state.PreKeyRecord
|
||||
import org.signal.libsignal.protocol.state.SignedPreKeyRecord
|
||||
import org.whispersystems.signalservice.api.push.ServiceIdType
|
||||
|
||||
/**
|
||||
* Holder class to pass around a bunch of prekeys that we send off to the service during registration.
|
||||
* As the service does not return the submitted prekeys,we need to hold them in memory so that when
|
||||
* the service approves the keys we have a local copy to persist.
|
||||
*/
|
||||
data class PreKeyCollection(
|
||||
val nextSignedPreKeyId: Int,
|
||||
val ecOneTimePreKeyIdOffset: Int,
|
||||
val lastResortKyberPreKeyId: Int,
|
||||
val oneTimeKyberPreKeyIdOffset: Int,
|
||||
val serviceIdType: ServiceIdType,
|
||||
val identityKey: IdentityKey,
|
||||
val signedPreKey: SignedPreKeyRecord,
|
||||
val oneTimeEcPreKeys: List<PreKeyRecord>,
|
||||
val lastResortKyberPreKey: KyberPreKeyRecord,
|
||||
val oneTimeKyberPreKeys: List<KyberPreKeyRecord>
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.account
|
||||
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
|
||||
/**
|
||||
* A holder class to hold onto two [PreKeyCollection] that are very similar but share an [IdentityKeyPair]
|
||||
*/
|
||||
data class PreKeyCollections(
|
||||
val identityKeyPair: IdentityKeyPair,
|
||||
val aciPreKeyCollection: PreKeyCollection,
|
||||
val pniPreKeyCollection: PreKeyCollection
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.signalservice.internal.push
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
|
||||
data class GcmRegistrationId(
|
||||
@JsonProperty val gcmRegistrationId: String,
|
||||
@JsonProperty val webSocketChannel: Boolean
|
||||
)
|
||||
@@ -40,8 +40,10 @@ import org.signal.storageservice.protos.groups.GroupExternalCredential;
|
||||
import org.signal.storageservice.protos.groups.GroupJoinInfo;
|
||||
import org.whispersystems.signalservice.api.account.AccountAttributes;
|
||||
import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest;
|
||||
import org.whispersystems.signalservice.api.account.PreKeyCollections;
|
||||
import org.whispersystems.signalservice.api.account.PniKeyDistributionRequest;
|
||||
import org.whispersystems.signalservice.api.account.PreKeyUpload;
|
||||
import org.whispersystems.signalservice.api.account.PreKeyCollection;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||
import org.whispersystems.signalservice.api.groupsv2.CredentialResponse;
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString;
|
||||
@@ -163,6 +165,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -391,21 +394,52 @@ public class PushServiceSocket {
|
||||
}
|
||||
}
|
||||
|
||||
public VerifyAccountResponse submitRegistrationRequest(@Nullable String sessionId, @Nullable String recoveryPassword, AccountAttributes attributes, boolean skipDeviceTransfer) throws IOException {
|
||||
public VerifyAccountResponse submitRegistrationRequest(@Nullable String sessionId, @Nullable String recoveryPassword, AccountAttributes attributes, PreKeyCollections preKeys, @Nullable String fcmToken, boolean skipDeviceTransfer) throws IOException {
|
||||
String path = REGISTRATION_PATH;
|
||||
if (sessionId == null && recoveryPassword == null) {
|
||||
throw new IllegalArgumentException("Neither Session ID nor Recovery Password provided.");
|
||||
|
||||
}
|
||||
|
||||
if (sessionId != null && recoveryPassword != null) {
|
||||
throw new IllegalArgumentException("You must supply one and only one of either: Session ID, or Recovery Password.");
|
||||
}
|
||||
|
||||
GcmRegistrationId gcmRegistrationId;
|
||||
if (attributes.getFetchesMessages()) {
|
||||
gcmRegistrationId = null;
|
||||
} else {
|
||||
gcmRegistrationId = new GcmRegistrationId(fcmToken, true);
|
||||
}
|
||||
|
||||
RegistrationSessionRequestBody body;
|
||||
if (sessionId != null) {
|
||||
body = new RegistrationSessionRequestBody(sessionId, null, attributes, skipDeviceTransfer);
|
||||
final PreKeyCollection aciPreKeys = preKeys.getAciPreKeyCollection();
|
||||
final PreKeyCollection pniPreKeys = preKeys.getPniPreKeyCollection();
|
||||
final SignedPreKeyEntity aciSignedPreKey = new SignedPreKeyEntity(Objects.requireNonNull(aciPreKeys.getSignedPreKey()).getId(),
|
||||
aciPreKeys.getSignedPreKey().getKeyPair().getPublicKey(),
|
||||
aciPreKeys.getSignedPreKey().getSignature());
|
||||
final SignedPreKeyEntity pniSignedPreKey = new SignedPreKeyEntity(Objects.requireNonNull(pniPreKeys.getSignedPreKey()).getId(),
|
||||
pniPreKeys.getSignedPreKey().getKeyPair().getPublicKey(),
|
||||
pniPreKeys.getSignedPreKey().getSignature());
|
||||
final KyberPreKeyEntity aciLastResortKyberPreKey = new KyberPreKeyEntity(Objects.requireNonNull(aciPreKeys.getLastResortKyberPreKey()).getId(),
|
||||
aciPreKeys.getLastResortKyberPreKey().getKeyPair().getPublicKey(),
|
||||
aciPreKeys.getLastResortKyberPreKey().getSignature());
|
||||
final KyberPreKeyEntity pniLastResortKyberPreKey = new KyberPreKeyEntity(Objects.requireNonNull(pniPreKeys.getLastResortKyberPreKey()).getId(),
|
||||
pniPreKeys.getLastResortKyberPreKey().getKeyPair().getPublicKey(),
|
||||
pniPreKeys.getLastResortKyberPreKey().getSignature());
|
||||
body = new RegistrationSessionRequestBody(sessionId,
|
||||
null,
|
||||
attributes,
|
||||
Base64.encodeBytesWithoutPadding(aciPreKeys.getIdentityKey().serialize()),
|
||||
Base64.encodeBytesWithoutPadding(pniPreKeys.getIdentityKey().serialize()),
|
||||
aciSignedPreKey,
|
||||
pniSignedPreKey,
|
||||
aciLastResortKyberPreKey,
|
||||
pniLastResortKyberPreKey,
|
||||
gcmRegistrationId,
|
||||
skipDeviceTransfer);
|
||||
} else {
|
||||
body = new RegistrationSessionRequestBody(null, recoveryPassword, attributes, skipDeviceTransfer);
|
||||
body = new RegistrationSessionRequestBody(null, recoveryPassword, attributes, null, null, null, null, null, null, null, skipDeviceTransfer);
|
||||
}
|
||||
|
||||
String response = makeServiceRequest(path, "POST", JsonUtil.toJson(body), NO_HEADERS, new RegistrationSessionResponseHandler(), Optional.empty());
|
||||
@@ -486,7 +520,7 @@ public class PushServiceSocket {
|
||||
JsonUtil.toJson(new ProvisioningMessage(Base64.encodeBytes(body))));
|
||||
}
|
||||
|
||||
public void registerGcmId(String gcmRegistrationId) throws IOException {
|
||||
public void registerGcmId(@Nonnull String gcmRegistrationId) throws IOException {
|
||||
GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId, true);
|
||||
makeServiceRequest(REGISTER_GCM_PATH, "PUT", JsonUtil.toJson(registration));
|
||||
}
|
||||
@@ -2271,22 +2305,6 @@ public class PushServiceSocket {
|
||||
return readBodyJson(response.body(), clazz);
|
||||
}
|
||||
|
||||
private static class GcmRegistrationId {
|
||||
|
||||
@JsonProperty
|
||||
private String gcmRegistrationId;
|
||||
|
||||
@JsonProperty
|
||||
private boolean webSocketChannel;
|
||||
|
||||
public GcmRegistrationId() {}
|
||||
|
||||
public GcmRegistrationId(String gcmRegistrationId, boolean webSocketChannel) {
|
||||
this.gcmRegistrationId = gcmRegistrationId;
|
||||
this.webSocketChannel = webSocketChannel;
|
||||
}
|
||||
}
|
||||
|
||||
public enum VerificationCodeTransport { SMS, VOICE }
|
||||
|
||||
private static class RegistrationLock {
|
||||
|
||||
@@ -3,11 +3,19 @@ package org.whispersystems.signalservice.internal.push
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import org.whispersystems.signalservice.api.account.AccountAttributes
|
||||
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
data class RegistrationSessionRequestBody(
|
||||
@JsonProperty val sessionId: String? = null,
|
||||
@JsonProperty val recoveryPassword: String? = null,
|
||||
@JsonProperty val accountAttributes: AccountAttributes,
|
||||
@JsonProperty val aciIdentityKey: String,
|
||||
@JsonProperty val pniIdentityKey: String,
|
||||
@JsonProperty val aciSignedPreKey: SignedPreKeyEntity,
|
||||
@JsonProperty val pniSignedPreKey: SignedPreKeyEntity,
|
||||
@JsonProperty val aciPqLastResortPreKey: KyberPreKeyEntity,
|
||||
@JsonProperty val pniPqLastResortPreKey: KyberPreKeyEntity,
|
||||
@JsonProperty val gcmToken: GcmRegistrationId?,
|
||||
@JsonProperty val skipDeviceTransfer: Boolean
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user