Skip to content

Commit 767cfbc

Browse files
Fix atomic registrations when not using session ID.
1 parent 55af6ca commit 767cfbc

File tree

8 files changed

+57
-78
lines changed

8 files changed

+57
-78
lines changed

app/src/main/java/org/thoughtcrime/securesms/registration/RegistrationData.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
package org.thoughtcrime.securesms.registration
22

33
import org.signal.libsignal.zkgroup.profiles.ProfileKey
4-
import org.whispersystems.signalservice.api.account.PreKeyCollections
4+
import org.whispersystems.signalservice.api.account.PreKeyCollection
55

66
data class RegistrationData(
77
val code: String,
88
val e164: String,
99
val password: String,
1010
val registrationId: Int,
1111
val profileKey: ProfileKey,
12-
val preKeyCollections: PreKeyCollections,
12+
val aciPreKeyCollection: PreKeyCollection,
13+
val pniPreKeyCollection: PreKeyCollection,
1314
val fcmToken: String?,
1415
val pniRegistrationId: Int,
1516
val recoveryPassword: String?

app/src/main/java/org/thoughtcrime/securesms/registration/RegistrationRepository.java

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
1616
import org.signal.libsignal.protocol.util.KeyHelper;
1717
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
18-
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
1918
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
2019
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
2120
import org.thoughtcrime.securesms.crypto.SenderKeyUtil;
@@ -40,8 +39,6 @@
4039
import org.whispersystems.signalservice.api.KbsPinData;
4140
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
4241
import org.whispersystems.signalservice.api.account.PreKeyCollection;
43-
import org.whispersystems.signalservice.api.account.PreKeyCollections;
44-
import org.whispersystems.signalservice.api.account.PreKeyUpload;
4542
import org.whispersystems.signalservice.api.push.ACI;
4643
import org.whispersystems.signalservice.api.push.PNI;
4744
import org.whispersystems.signalservice.api.push.ServiceIdType;
@@ -148,11 +145,11 @@ private void registerAccountInternal(@NonNull RegistrationData registrationData,
148145
SenderKeyUtil.clearAllState();
149146

150147
SignalServiceAccountDataStoreImpl aciProtocolStore = ApplicationDependencies.getProtocolStore().aci();
151-
PreKeyCollection aciPreKeyCollection = registrationData.getPreKeyCollections().getAciPreKeyCollection();
148+
PreKeyCollection aciPreKeyCollection = registrationData.getAciPreKeyCollection();
152149
PreKeyMetadataStore aciMetadataStore = SignalStore.account().aciPreKeys();
153150

154151
SignalServiceAccountDataStoreImpl pniProtocolStore = ApplicationDependencies.getProtocolStore().pni();
155-
PreKeyCollection pniPreKeyCollection = registrationData.getPreKeyCollections().getPniPreKeyCollection();
152+
PreKeyCollection pniPreKeyCollection = registrationData.getPniPreKeyCollection();
156153
PreKeyMetadataStore pniMetadataStore = SignalStore.account().pniPreKeys();
157154

158155
storePreKeys(aciProtocolStore, aciMetadataStore, aciPreKeyCollection);
@@ -189,23 +186,24 @@ private void registerAccountInternal(@NonNull RegistrationData registrationData,
189186
ApplicationDependencies.getIncomingMessageObserver();
190187
}
191188

192-
public static @Nullable PreKeyCollections generatePreKeys() {
193-
final IdentityKeyPair keyPair = IdentityKeyUtil.generateIdentityKeyPair();
194-
final PreKeyMetadataStore aciMetadataStore = SignalStore.account().aciPreKeys();
195-
final PreKeyMetadataStore pniMetadataStore = SignalStore.account().pniPreKeys();
196-
197-
try {
198-
return new PreKeyCollections(keyPair,
199-
generatePreKeysForType(ServiceIdType.ACI, keyPair, aciMetadataStore),
200-
generatePreKeysForType(ServiceIdType.PNI, keyPair, pniMetadataStore)
201-
);
202-
} catch (IOException e) {
203-
Log.e(TAG, "Failed to generate prekeys!", e);
204-
return null;
189+
public static PreKeyCollection generatePreKeysForType(ServiceIdType serviceIdType) {
190+
IdentityKeyPair keyPair;
191+
PreKeyMetadataStore metadataStore;
192+
if (serviceIdType == ServiceIdType.ACI) {
193+
if (!SignalStore.account().hasAciIdentityKey()) {
194+
SignalStore.account().generateAciIdentityKeyIfNecessary();
195+
}
196+
keyPair = SignalStore.account().getAciIdentityKey();
197+
metadataStore = SignalStore.account().aciPreKeys();
198+
} else if (serviceIdType == ServiceIdType.PNI) {
199+
if (!SignalStore.account().hasPniIdentityKey()) {
200+
SignalStore.account().generatePniIdentityKeyIfNecessary();
201+
}
202+
keyPair = SignalStore.account().getPniIdentityKey();
203+
metadataStore = SignalStore.account().pniPreKeys();
204+
} else {
205+
throw new IllegalArgumentException("serviceIdType is not one of {ACI, PNI}");
205206
}
206-
}
207-
208-
private static PreKeyCollection generatePreKeysForType(ServiceIdType serviceIdType, IdentityKeyPair keyPair, PreKeyMetadataStore metadataStore) throws IOException {
209207
int nextSignedPreKeyId = metadataStore.getNextSignedPreKeyId();
210208
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(nextSignedPreKeyId, keyPair.getPrivateKey());
211209
metadataStore.setActiveSignedPreKeyId(signedPreKey.getId());
@@ -222,6 +220,7 @@ private static PreKeyCollection generatePreKeysForType(ServiceIdType serviceIdTy
222220
List<KyberPreKeyRecord> oneTimeKyberPreKeys = PreKeyUtil.generateOneTimeKyberPreKeyRecords(oneTimeKyberPreKeyIdOffset, keyPair.getPrivateKey());
223221

224222
return new PreKeyCollection(
223+
keyPair,
225224
nextSignedPreKeyId,
226225
ecOneTimePreKeyIdOffset,
227226
nextKyberPreKeyId,

app/src/main/java/org/thoughtcrime/securesms/registration/VerifyAccountRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ class VerifyAccountRepository(private val context: Application) {
186186
)
187187

188188
return Single.fromCallable {
189-
val response = accountManager.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, registrationData.preKeyCollections, registrationData.fcmToken, true)
189+
val response = accountManager.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, registrationData.aciPreKeyCollection, registrationData.pniPreKeyCollection, registrationData.fcmToken, true)
190190
VerifyResponse.from(response, kbsData, pin)
191191
}.subscribeOn(Schedulers.io())
192192
}

app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.whispersystems.signalservice.api.KbsPinData;
3333
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
3434
import org.whispersystems.signalservice.api.kbs.PinHashUtil;
35+
import org.whispersystems.signalservice.api.push.ServiceIdType;
3536
import org.whispersystems.signalservice.api.push.exceptions.IncorrectCodeException;
3637
import org.whispersystems.signalservice.internal.ServiceResponse;
3738
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
@@ -235,7 +236,8 @@ private RegistrationData getRegistrationData() {
235236
getRegistrationSecret(),
236237
registrationRepository.getRegistrationId(),
237238
registrationRepository.getProfileKey(getNumber().getE164Number()),
238-
Objects.requireNonNull(RegistrationRepository.generatePreKeys()),
239+
RegistrationRepository.generatePreKeysForType(ServiceIdType.ACI),
240+
RegistrationRepository.generatePreKeysForType(ServiceIdType.PNI),
239241
getFcmToken(),
240242
registrationRepository.getPniRegistrationId(),
241243
getSessionId() != null ? null : getRecoveryPassword());

libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import org.whispersystems.signalservice.api.account.AccountAttributes;
2020
import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest;
2121
import org.whispersystems.signalservice.api.account.PniKeyDistributionRequest;
22-
import org.whispersystems.signalservice.api.account.PreKeyCollections;
22+
import org.whispersystems.signalservice.api.account.PreKeyCollection;
2323
import org.whispersystems.signalservice.api.account.PreKeyUpload;
2424
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
2525
import org.whispersystems.signalservice.api.crypto.ProfileCipherOutputStream;
@@ -317,9 +317,9 @@ public ServiceResponse<RegistrationSessionMetadataResponse> verifyAccount(@Nonnu
317317
}
318318
}
319319

320-
public @Nonnull ServiceResponse<VerifyAccountResponse> registerAccount(@Nullable String sessionId, @Nullable String recoveryPassword, AccountAttributes attributes, PreKeyCollections preKeys, String fcmToken, boolean skipDeviceTransfer) {
320+
public @Nonnull ServiceResponse<VerifyAccountResponse> registerAccount(@Nullable String sessionId, @Nullable String recoveryPassword, AccountAttributes attributes, PreKeyCollection aciPreKeys, PreKeyCollection pniPreKeys, String fcmToken, boolean skipDeviceTransfer) {
321321
try {
322-
VerifyAccountResponse response = pushServiceSocket.submitRegistrationRequest(sessionId, recoveryPassword, attributes, preKeys, fcmToken, skipDeviceTransfer);
322+
VerifyAccountResponse response = pushServiceSocket.submitRegistrationRequest(sessionId, recoveryPassword, attributes, aciPreKeys, pniPreKeys, fcmToken, skipDeviceTransfer);
323323
return ServiceResponse.forResult(response, 200, null);
324324
} catch (IOException e) {
325325
return ServiceResponse.forUnknownError(e);

libsignal/service/src/main/java/org/whispersystems/signalservice/api/account/PreKeyCollection.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package org.whispersystems.signalservice.api.account
77

88
import org.signal.libsignal.protocol.IdentityKey
9+
import org.signal.libsignal.protocol.IdentityKeyPair
910
import org.signal.libsignal.protocol.state.KyberPreKeyRecord
1011
import org.signal.libsignal.protocol.state.PreKeyRecord
1112
import org.signal.libsignal.protocol.state.SignedPreKeyRecord
@@ -17,6 +18,7 @@ import org.whispersystems.signalservice.api.push.ServiceIdType
1718
* the service approves the keys we have a local copy to persist.
1819
*/
1920
data class PreKeyCollection(
21+
val identityKeyPair: IdentityKeyPair,
2022
val nextSignedPreKeyId: Int,
2123
val ecOneTimePreKeyIdOffset: Int,
2224
val lastResortKyberPreKeyId: Int,

libsignal/service/src/main/java/org/whispersystems/signalservice/api/account/PreKeyCollections.kt

Lines changed: 0 additions & 17 deletions
This file was deleted.

libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@
4040
import org.signal.storageservice.protos.groups.GroupJoinInfo;
4141
import org.whispersystems.signalservice.api.account.AccountAttributes;
4242
import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest;
43-
import org.whispersystems.signalservice.api.account.PreKeyCollections;
4443
import org.whispersystems.signalservice.api.account.PniKeyDistributionRequest;
45-
import org.whispersystems.signalservice.api.account.PreKeyUpload;
4644
import org.whispersystems.signalservice.api.account.PreKeyCollection;
45+
import org.whispersystems.signalservice.api.account.PreKeyUpload;
4746
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
4847
import org.whispersystems.signalservice.api.groupsv2.CredentialResponse;
4948
import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString;
@@ -394,7 +393,7 @@ public RegistrationSessionMetadataResponse submitVerificationCode(String session
394393
}
395394
}
396395

397-
public VerifyAccountResponse submitRegistrationRequest(@Nullable String sessionId, @Nullable String recoveryPassword, AccountAttributes attributes, PreKeyCollections preKeys, @Nullable String fcmToken, boolean skipDeviceTransfer) throws IOException {
396+
public VerifyAccountResponse submitRegistrationRequest(@Nullable String sessionId, @Nullable String recoveryPassword, AccountAttributes attributes, PreKeyCollection aciPreKeys, PreKeyCollection pniPreKeys, @Nullable String fcmToken, boolean skipDeviceTransfer) throws IOException {
398397
String path = REGISTRATION_PATH;
399398
if (sessionId == null && recoveryPassword == null) {
400399
throw new IllegalArgumentException("Neither Session ID nor Recovery Password provided.");
@@ -411,36 +410,29 @@ public VerifyAccountResponse submitRegistrationRequest(@Nullable String sessionI
411410
gcmRegistrationId = new GcmRegistrationId(fcmToken, true);
412411
}
413412

414-
RegistrationSessionRequestBody body;
415-
if (sessionId != null) {
416-
final PreKeyCollection aciPreKeys = preKeys.getAciPreKeyCollection();
417-
final PreKeyCollection pniPreKeys = preKeys.getPniPreKeyCollection();
418-
final SignedPreKeyEntity aciSignedPreKey = new SignedPreKeyEntity(Objects.requireNonNull(aciPreKeys.getSignedPreKey()).getId(),
419-
aciPreKeys.getSignedPreKey().getKeyPair().getPublicKey(),
420-
aciPreKeys.getSignedPreKey().getSignature());
421-
final SignedPreKeyEntity pniSignedPreKey = new SignedPreKeyEntity(Objects.requireNonNull(pniPreKeys.getSignedPreKey()).getId(),
422-
pniPreKeys.getSignedPreKey().getKeyPair().getPublicKey(),
423-
pniPreKeys.getSignedPreKey().getSignature());
424-
final KyberPreKeyEntity aciLastResortKyberPreKey = new KyberPreKeyEntity(Objects.requireNonNull(aciPreKeys.getLastResortKyberPreKey()).getId(),
425-
aciPreKeys.getLastResortKyberPreKey().getKeyPair().getPublicKey(),
426-
aciPreKeys.getLastResortKyberPreKey().getSignature());
427-
final KyberPreKeyEntity pniLastResortKyberPreKey = new KyberPreKeyEntity(Objects.requireNonNull(pniPreKeys.getLastResortKyberPreKey()).getId(),
428-
pniPreKeys.getLastResortKyberPreKey().getKeyPair().getPublicKey(),
429-
pniPreKeys.getLastResortKyberPreKey().getSignature());
430-
body = new RegistrationSessionRequestBody(sessionId,
431-
null,
432-
attributes,
433-
Base64.encodeBytesWithoutPadding(aciPreKeys.getIdentityKey().serialize()),
434-
Base64.encodeBytesWithoutPadding(pniPreKeys.getIdentityKey().serialize()),
435-
aciSignedPreKey,
436-
pniSignedPreKey,
437-
aciLastResortKyberPreKey,
438-
pniLastResortKyberPreKey,
439-
gcmRegistrationId,
440-
skipDeviceTransfer);
441-
} else {
442-
body = new RegistrationSessionRequestBody(null, recoveryPassword, attributes, null, null, null, null, null, null, null, skipDeviceTransfer);
443-
}
413+
final SignedPreKeyEntity aciSignedPreKey = new SignedPreKeyEntity(Objects.requireNonNull(aciPreKeys.getSignedPreKey()).getId(),
414+
aciPreKeys.getSignedPreKey().getKeyPair().getPublicKey(),
415+
aciPreKeys.getSignedPreKey().getSignature());
416+
final SignedPreKeyEntity pniSignedPreKey = new SignedPreKeyEntity(Objects.requireNonNull(pniPreKeys.getSignedPreKey()).getId(),
417+
pniPreKeys.getSignedPreKey().getKeyPair().getPublicKey(),
418+
pniPreKeys.getSignedPreKey().getSignature());
419+
final KyberPreKeyEntity aciLastResortKyberPreKey = new KyberPreKeyEntity(Objects.requireNonNull(aciPreKeys.getLastResortKyberPreKey()).getId(),
420+
aciPreKeys.getLastResortKyberPreKey().getKeyPair().getPublicKey(),
421+
aciPreKeys.getLastResortKyberPreKey().getSignature());
422+
final KyberPreKeyEntity pniLastResortKyberPreKey = new KyberPreKeyEntity(Objects.requireNonNull(pniPreKeys.getLastResortKyberPreKey()).getId(),
423+
pniPreKeys.getLastResortKyberPreKey().getKeyPair().getPublicKey(),
424+
pniPreKeys.getLastResortKyberPreKey().getSignature());
425+
RegistrationSessionRequestBody body = new RegistrationSessionRequestBody(sessionId,
426+
recoveryPassword,
427+
attributes,
428+
Base64.encodeBytesWithoutPadding(aciPreKeys.getIdentityKey().serialize()),
429+
Base64.encodeBytesWithoutPadding(pniPreKeys.getIdentityKey().serialize()),
430+
aciSignedPreKey,
431+
pniSignedPreKey,
432+
aciLastResortKyberPreKey,
433+
pniLastResortKyberPreKey,
434+
gcmRegistrationId,
435+
skipDeviceTransfer);
444436

445437
String response = makeServiceRequest(path, "POST", JsonUtil.toJson(body), NO_HEADERS, new RegistrationSessionResponseHandler(), Optional.empty());
446438
return JsonUtil.fromJson(response, VerifyAccountResponse.class);

0 commit comments

Comments
 (0)