Fix prekey generation during registration.
This commit is contained in:
parent
61cd9767c8
commit
f438ef543b
12 changed files with 137 additions and 126 deletions
|
@ -141,7 +141,13 @@ class ChangeNumberRepository(
|
|||
}
|
||||
}
|
||||
|
||||
VerifyResponse.from(changeNumberResponse, null, null)
|
||||
VerifyResponse.from(
|
||||
response = changeNumberResponse,
|
||||
kbsData = null,
|
||||
pin = null,
|
||||
aciPreKeyCollection = null,
|
||||
pniPreKeyCollection = null
|
||||
)
|
||||
}.subscribeOn(Schedulers.single())
|
||||
.onErrorReturn { t -> ServiceResponse.forExecutionError(t) }
|
||||
}
|
||||
|
@ -191,7 +197,13 @@ class ChangeNumberRepository(
|
|||
}
|
||||
}
|
||||
|
||||
VerifyResponse.from(changeNumberResponse, kbsData, pin)
|
||||
VerifyResponse.from(
|
||||
response = changeNumberResponse,
|
||||
kbsData = kbsData,
|
||||
pin = pin,
|
||||
aciPreKeyCollection = null,
|
||||
pniPreKeyCollection = null
|
||||
)
|
||||
}.subscribeOn(Schedulers.single())
|
||||
.onErrorReturn { t -> ServiceResponse.forExecutionError(t) }
|
||||
}
|
||||
|
@ -341,7 +353,7 @@ class ChangeNumberRepository(
|
|||
val lastResortKyberPreKeyRecord: KyberPreKeyRecord = if (deviceId == primaryDeviceId) {
|
||||
PreKeyUtil.generateAndStoreLastResortKyberPreKey(ApplicationDependencies.getProtocolStore().pni(), SignalStore.account().pniPreKeys, pniIdentity.privateKey)
|
||||
} else {
|
||||
PreKeyUtil.generateKyberPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), pniIdentity.privateKey)
|
||||
PreKeyUtil.generateLastRestortKyberPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), pniIdentity.privateKey)
|
||||
}
|
||||
devicePniLastResortKyberPreKeys[deviceId] = KyberPreKeyEntity(lastResortKyberPreKeyRecord.id, lastResortKyberPreKeyRecord.keyPair.publicKey, lastResortKyberPreKeyRecord.signature)
|
||||
|
||||
|
|
|
@ -50,21 +50,21 @@ 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);
|
||||
int startingId = metadataStore.getNextEcOneTimePreKeyId();
|
||||
final List<PreKeyRecord> records = generateOneTimeEcPreKeys(startingId);
|
||||
|
||||
storeOneTimeEcPreKeys(protocolStore, metadataStore, preKeyIdOffset, records);
|
||||
storeOneTimeEcPreKeys(protocolStore, metadataStore, records);
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
public synchronized static List<PreKeyRecord> generateOneTimeEcPreKeys(int preKeyIdOffset) {
|
||||
public synchronized static List<PreKeyRecord> generateOneTimeEcPreKeys(int startingId) {
|
||||
Log.i(TAG, "Generating one-time EC prekeys...");
|
||||
|
||||
List<PreKeyRecord> records = new ArrayList<>(BATCH_SIZE);
|
||||
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
int preKeyId = (preKeyIdOffset + i) % Medium.MAX_VALUE;
|
||||
int preKeyId = (startingId + i) % Medium.MAX_VALUE;
|
||||
ECKeyPair keyPair = Curve.generateKeyPair();
|
||||
PreKeyRecord record = new PreKeyRecord(preKeyId, keyPair);
|
||||
|
||||
|
@ -74,47 +74,64 @@ public class PreKeyUtil {
|
|||
return records;
|
||||
}
|
||||
|
||||
public synchronized static void storeOneTimeEcPreKeys(@NonNull SignalProtocolStore protocolStore, PreKeyMetadataStore metadataStore, int preKeyIdOffset, List<PreKeyRecord> prekeys) {
|
||||
public synchronized static void storeOneTimeEcPreKeys(@NonNull SignalProtocolStore protocolStore, PreKeyMetadataStore metadataStore, List<PreKeyRecord> prekeys) {
|
||||
Log.i(TAG, "Storing one-time EC prekeys...");
|
||||
|
||||
if (prekeys.isEmpty()) {
|
||||
Log.w(TAG, "Empty list of one-time EC prekeys! Nothing to store.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (PreKeyRecord record : prekeys) {
|
||||
protocolStore.storePreKey(record.getId(), record);
|
||||
}
|
||||
metadataStore.setNextEcOneTimePreKeyId((preKeyIdOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE);
|
||||
|
||||
int lastId = prekeys.get(prekeys.size() - 1).getId();
|
||||
|
||||
metadataStore.setNextEcOneTimePreKeyId((lastId + 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());
|
||||
int startingId = metadataStore.getNextKyberPreKeyId();
|
||||
List<KyberPreKeyRecord> records = generateOneTimeKyberPreKeyRecords(startingId, protocolStore.getIdentityKeyPair().getPrivateKey());
|
||||
|
||||
storeOneTimeKyberPreKeys(protocolStore, metadataStore, preKeyIdOffset, records);
|
||||
storeOneTimeKyberPreKeys(protocolStore, metadataStore, records);
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static List<KyberPreKeyRecord> generateOneTimeKyberPreKeyRecords(int preKeyIdOffset, @NonNull ECPrivateKey privateKey) {
|
||||
public static List<KyberPreKeyRecord> generateOneTimeKyberPreKeyRecords(int startingId, @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;
|
||||
int preKeyId = (startingId + 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) {
|
||||
public synchronized static void storeOneTimeKyberPreKeys(@NonNull SignalProtocolStore protocolStore, PreKeyMetadataStore metadataStore, List<KyberPreKeyRecord> prekeys) {
|
||||
Log.i(TAG, "Storing one-time kyber prekeys...");
|
||||
|
||||
if (prekeys.isEmpty()) {
|
||||
Log.w(TAG, "Empty list of kyber prekeys! Nothing to store.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (KyberPreKeyRecord record : prekeys) {
|
||||
protocolStore.storeKyberPreKey(record.getId(), record);
|
||||
}
|
||||
metadataStore.setNextKyberPreKeyId((preKeyIdOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE);
|
||||
|
||||
int lastId = prekeys.get(prekeys.size() - 1).getId();
|
||||
|
||||
metadataStore.setNextKyberPreKeyId((lastId + 1) % Medium.MAX_VALUE);
|
||||
}
|
||||
|
||||
public synchronized static @NonNull SignedPreKeyRecord generateAndStoreSignedPreKey(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore) {
|
||||
|
@ -127,7 +144,8 @@ public class PreKeyUtil {
|
|||
{
|
||||
int signedPreKeyId = metadataStore.getNextSignedPreKeyId();
|
||||
SignedPreKeyRecord record = generateSignedPreKey(signedPreKeyId, privateKey);
|
||||
storeSignedPreKey(protocolStore, metadataStore, signedPreKeyId, record);
|
||||
|
||||
storeSignedPreKey(protocolStore, metadataStore, record);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
@ -145,11 +163,11 @@ public class PreKeyUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public synchronized static void storeSignedPreKey(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore, int signedPreKeyId, SignedPreKeyRecord record) {
|
||||
Log.i(TAG, "Storing signed prekeys...");
|
||||
public synchronized static void storeSignedPreKey(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore, SignedPreKeyRecord record) {
|
||||
Log.i(TAG, "Storing signed prekey...");
|
||||
|
||||
protocolStore.storeSignedPreKey(signedPreKeyId, record);
|
||||
metadataStore.setNextSignedPreKeyId((signedPreKeyId + 1) % Medium.MAX_VALUE);
|
||||
protocolStore.storeSignedPreKey(record.getId(), record);
|
||||
metadataStore.setNextSignedPreKeyId((record.getId() + 1) % Medium.MAX_VALUE);
|
||||
}
|
||||
|
||||
public synchronized static @NonNull KyberPreKeyRecord generateAndStoreLastResortKyberPreKey(@NonNull SignalServiceAccountDataStore protocolStore, @NonNull PreKeyMetadataStore metadataStore) {
|
||||
|
@ -162,24 +180,28 @@ public class PreKeyUtil {
|
|||
{
|
||||
int id = metadataStore.getNextKyberPreKeyId();
|
||||
KyberPreKeyRecord record = generateKyberPreKey(id, privateKey);
|
||||
storeLastResortKyberPreKey(protocolStore, metadataStore, id, record);
|
||||
|
||||
storeLastResortKyberPreKey(protocolStore, metadataStore, record);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
public synchronized static @NonNull KyberPreKeyRecord generateKyberPreKey(int id, @NonNull ECPrivateKey privateKey) {
|
||||
Log.i(TAG, "Generating kyber prekeys...");
|
||||
public synchronized static @NonNull KyberPreKeyRecord generateLastRestortKyberPreKey(int id, @NonNull ECPrivateKey privateKey) {
|
||||
Log.i(TAG, "Generating last resort kyber prekey...");
|
||||
return generateKyberPreKey(id, privateKey);
|
||||
}
|
||||
|
||||
private synchronized static @NonNull KyberPreKeyRecord generateKyberPreKey(int id, @NonNull ECPrivateKey privateKey) {
|
||||
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) {
|
||||
public synchronized static void storeLastResortKyberPreKey(@NonNull SignalServiceAccountDataStore protocolStore, @NonNull PreKeyMetadataStore metadataStore, KyberPreKeyRecord record) {
|
||||
Log.i(TAG, "Storing kyber prekeys...");
|
||||
protocolStore.storeKyberPreKey(id, record);
|
||||
metadataStore.setNextKyberPreKeyId((id + 1) % Medium.MAX_VALUE);
|
||||
protocolStore.storeKyberPreKey(record.getId(), record);
|
||||
metadataStore.setNextKyberPreKeyId((record.getId() + 1) % Medium.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -150,7 +150,13 @@ class PnpInitializeDevicesJob private constructor(parameters: Parameters) : Base
|
|||
}
|
||||
}
|
||||
|
||||
VerifyResponse.from(distributionResponse, null, null)
|
||||
VerifyResponse.from(
|
||||
response = distributionResponse,
|
||||
kbsData = null,
|
||||
pin = null,
|
||||
aciPreKeyCollection = null,
|
||||
pniPreKeyCollection = null
|
||||
)
|
||||
}.subscribeOn(Schedulers.single())
|
||||
.onErrorReturn { t -> ServiceResponse.forExecutionError(t) }
|
||||
}
|
||||
|
@ -188,7 +194,7 @@ class PnpInitializeDevicesJob private constructor(parameters: Parameters) : Base
|
|||
val lastResortKyberPreKeyRecord: KyberPreKeyRecord = if (deviceId == primaryDeviceId) {
|
||||
pniProtocolStore.loadKyberPreKey(SignalStore.account().pniPreKeys.lastResortKyberPreKeyId)
|
||||
} else {
|
||||
PreKeyUtil.generateKyberPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), pniIdentity.privateKey)
|
||||
PreKeyUtil.generateLastRestortKyberPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), pniIdentity.privateKey)
|
||||
}
|
||||
devicePniLastResortKyberPreKeys[deviceId] = KyberPreKeyEntity(lastResortKyberPreKeyRecord.id, lastResortKyberPreKeyRecord.keyPair.publicKey, lastResortKyberPreKeyRecord.signature)
|
||||
|
||||
|
|
|
@ -204,7 +204,7 @@ class PreKeysSyncJob private constructor(parameters: Parameters) : BaseJob(param
|
|||
log(serviceIdType, "Rotating last-resort kyber prekey. TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS)} days)")
|
||||
PreKeyUtil.generateAndStoreLastResortKyberPreKey(protocolStore, metadataStore)
|
||||
} else {
|
||||
log(serviceIdType, "No need to rotate signed prekey. TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS)} days)")
|
||||
log(serviceIdType, "No need to rotate last-resort kyber prekey. TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS)} days)")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.thoughtcrime.securesms.registration
|
||||
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey
|
||||
import org.whispersystems.signalservice.api.account.PreKeyCollection
|
||||
|
||||
data class RegistrationData(
|
||||
val code: String,
|
||||
|
@ -9,8 +8,6 @@ data class RegistrationData(
|
|||
val password: String,
|
||||
val registrationId: Int,
|
||||
val profileKey: ProfileKey,
|
||||
val aciPreKeyCollection: PreKeyCollection,
|
||||
val pniPreKeyCollection: PreKeyCollection,
|
||||
val fcmToken: String?,
|
||||
val pniRegistrationId: Int,
|
||||
val recoveryPassword: String?
|
||||
|
|
|
@ -11,10 +11,8 @@ 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.SignedPreKeyRecord;
|
||||
import org.signal.libsignal.protocol.util.KeyHelper;
|
||||
import org.signal.libsignal.protocol.util.Medium;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
|
@ -27,6 +25,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase;
|
|||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
||||
|
@ -37,16 +36,14 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
|||
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.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.account.PreKeyCollection;
|
||||
import org.whispersystems.signalservice.api.push.ACI;
|
||||
import org.whispersystems.signalservice.api.push.PNI;
|
||||
import org.whispersystems.signalservice.api.push.ServiceIdType;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.util.Preconditions;
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||
import org.whispersystems.signalservice.internal.push.BackupAuthCheckProcessor;
|
||||
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
@ -106,7 +103,7 @@ public final class RegistrationRepository {
|
|||
return Single.<ServiceResponse<VerifyResponse>>fromCallable(() -> {
|
||||
try {
|
||||
String pin = response.getPin();
|
||||
registerAccountInternal(registrationData, response.getVerifyAccountResponse(), pin, response.getKbsData(), setRegistrationLockEnabled);
|
||||
registerAccountInternal(registrationData, response, setRegistrationLockEnabled);
|
||||
|
||||
if (pin != null && !pin.isEmpty()) {
|
||||
PinState.onPinChangedOrCreated(context, pin, SignalStore.pinValues().getKeyboardType());
|
||||
|
@ -128,15 +125,16 @@ public final class RegistrationRepository {
|
|||
|
||||
@WorkerThread
|
||||
private void registerAccountInternal(@NonNull RegistrationData registrationData,
|
||||
@NonNull VerifyAccountResponse response,
|
||||
@Nullable String pin,
|
||||
@Nullable KbsPinData kbsData,
|
||||
@NonNull VerifyResponse response,
|
||||
boolean setRegistrationLockEnabled)
|
||||
throws IOException
|
||||
{
|
||||
ACI aci = ACI.parseOrThrow(response.getUuid());
|
||||
PNI pni = PNI.parseOrThrow(response.getPni());
|
||||
boolean hasPin = response.isStorageCapable();
|
||||
Preconditions.checkNotNull(response.getAciPreKeyCollection(), "Missing ACI prekey collection!");
|
||||
Preconditions.checkNotNull(response.getPniPreKeyCollection(), "Missing PNI prekey collection!");
|
||||
|
||||
ACI aci = ACI.parseOrThrow(response.getVerifyAccountResponse().getUuid());
|
||||
PNI pni = PNI.parseOrThrow(response.getVerifyAccountResponse().getPni());
|
||||
boolean hasPin = response.getVerifyAccountResponse().isStorageCapable();
|
||||
|
||||
SignalStore.account().setAci(aci);
|
||||
SignalStore.account().setPni(pni);
|
||||
|
@ -145,17 +143,14 @@ public final class RegistrationRepository {
|
|||
ApplicationDependencies.getProtocolStore().pni().sessions().archiveAllSessions();
|
||||
SenderKeyUtil.clearAllState();
|
||||
|
||||
SignalServiceAccountDataStoreImpl aciProtocolStore = ApplicationDependencies.getProtocolStore().aci();
|
||||
PreKeyCollection aciPreKeyCollection = registrationData.getAciPreKeyCollection();
|
||||
PreKeyMetadataStore aciMetadataStore = SignalStore.account().aciPreKeys();
|
||||
SignalServiceAccountDataStoreImpl aciProtocolStore = ApplicationDependencies.getProtocolStore().aci();
|
||||
PreKeyMetadataStore aciMetadataStore = SignalStore.account().aciPreKeys();
|
||||
|
||||
SignalServiceAccountDataStoreImpl pniProtocolStore = ApplicationDependencies.getProtocolStore().pni();
|
||||
PreKeyCollection pniPreKeyCollection = registrationData.getPniPreKeyCollection();
|
||||
PreKeyMetadataStore pniMetadataStore = SignalStore.account().pniPreKeys();
|
||||
|
||||
storePreKeys(aciProtocolStore, aciMetadataStore, aciPreKeyCollection);
|
||||
storePreKeys(pniProtocolStore, pniMetadataStore, pniPreKeyCollection);
|
||||
SignalServiceAccountDataStoreImpl pniProtocolStore = ApplicationDependencies.getProtocolStore().pni();
|
||||
PreKeyMetadataStore pniMetadataStore = SignalStore.account().pniPreKeys();
|
||||
|
||||
storeSignedAndLastResortPreKeys(aciProtocolStore, aciMetadataStore, response.getAciPreKeyCollection());
|
||||
storeSignedAndLastResortPreKeys(pniProtocolStore, pniMetadataStore, response.getPniPreKeyCollection());
|
||||
|
||||
RecipientTable recipientTable = SignalDatabase.recipients();
|
||||
RecipientId selfId = Recipient.trustedPush(aci, pni, registrationData.getE164()).getId();
|
||||
|
@ -181,66 +176,33 @@ public final class RegistrationRepository {
|
|||
TextSecurePreferences.setUnauthorizedReceived(context, false);
|
||||
NotificationManagerCompat.from(context).cancel(NotificationIds.UNREGISTERED_NOTIFICATION_ID);
|
||||
|
||||
PinState.onRegistration(context, kbsData, pin, hasPin, setRegistrationLockEnabled);
|
||||
PinState.onRegistration(context, response.getKbsData(), response.getPin(), hasPin, setRegistrationLockEnabled);
|
||||
|
||||
ApplicationDependencies.closeConnections();
|
||||
ApplicationDependencies.getIncomingMessageObserver();
|
||||
PreKeysSyncJob.enqueue();
|
||||
}
|
||||
|
||||
public static PreKeyCollection generatePreKeysForType(ServiceIdType serviceIdType) {
|
||||
IdentityKeyPair keyPair;
|
||||
PreKeyMetadataStore metadataStore;
|
||||
if (serviceIdType == ServiceIdType.ACI) {
|
||||
if (!SignalStore.account().hasAciIdentityKey()) {
|
||||
SignalStore.account().generateAciIdentityKeyIfNecessary();
|
||||
}
|
||||
keyPair = SignalStore.account().getAciIdentityKey();
|
||||
metadataStore = SignalStore.account().aciPreKeys();
|
||||
} else if (serviceIdType == ServiceIdType.PNI) {
|
||||
if (!SignalStore.account().hasPniIdentityKey()) {
|
||||
SignalStore.account().generatePniIdentityKeyIfNecessary();
|
||||
}
|
||||
keyPair = SignalStore.account().getPniIdentityKey();
|
||||
metadataStore = SignalStore.account().pniPreKeys();
|
||||
} else {
|
||||
throw new IllegalArgumentException("serviceIdType is not one of {ACI, PNI}");
|
||||
}
|
||||
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 = (nextKyberPreKeyId + 1) % Medium.MAX_VALUE;
|
||||
List<KyberPreKeyRecord> oneTimeKyberPreKeys = PreKeyUtil.generateOneTimeKyberPreKeyRecords(oneTimeKyberPreKeyIdOffset, keyPair.getPrivateKey());
|
||||
public static PreKeyCollection generateSignedAndLastResortPreKeys(IdentityKeyPair identity, PreKeyMetadataStore metadataStore) {
|
||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(metadataStore.getNextSignedPreKeyId(), identity.getPrivateKey());
|
||||
KyberPreKeyRecord lastResortKyberPreKey = PreKeyUtil.generateLastRestortKyberPreKey(metadataStore.getNextKyberPreKeyId(), identity.getPrivateKey());
|
||||
|
||||
return new PreKeyCollection(
|
||||
keyPair,
|
||||
nextSignedPreKeyId,
|
||||
ecOneTimePreKeyIdOffset,
|
||||
nextKyberPreKeyId,
|
||||
oneTimeKyberPreKeyIdOffset,
|
||||
serviceIdType,
|
||||
keyPair.getPublicKey(),
|
||||
identity.getPublicKey(),
|
||||
signedPreKey,
|
||||
oneTimeEcPreKeys,
|
||||
lastResortKyberPreKey,
|
||||
oneTimeKyberPreKeys
|
||||
lastResortKyberPreKey
|
||||
);
|
||||
}
|
||||
|
||||
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());
|
||||
private static void storeSignedAndLastResortPreKeys(SignalServiceAccountDataStoreImpl protocolStore, PreKeyMetadataStore metadataStore, PreKeyCollection preKeyCollection) {
|
||||
PreKeyUtil.storeSignedPreKey(protocolStore, metadataStore, preKeyCollection.getSignedPreKey());
|
||||
metadataStore.setSignedPreKeyRegistered(true);
|
||||
metadataStore.setActiveSignedPreKeyId(preKeyCollection.getSignedPreKey().getId());
|
||||
metadataStore.setLastSignedPreKeyRotationTime(System.currentTimeMillis());
|
||||
|
||||
PreKeyUtil.storeLastResortKyberPreKey(protocolStore, metadataStore, preKeyCollection.getLastResortKyberPreKey());
|
||||
metadataStore.setLastResortKyberPreKeyId(preKeyCollection.getLastResortKyberPreKey().getId());
|
||||
metadataStore.setLastResortKyberPreKeyRotationTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private void saveOwnIdentityKey(@NonNull RecipientId selfId, @NonNull SignalServiceAccountDataStoreImpl protocolStore, long now) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers
|
|||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
import org.thoughtcrime.securesms.AppCapabilities
|
||||
import org.thoughtcrime.securesms.gcm.FcmUtil
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
|
@ -185,9 +186,18 @@ class VerifyAccountRepository(private val context: Application) {
|
|||
recoveryPassword = registrationData.recoveryPassword
|
||||
)
|
||||
|
||||
SignalStore.account().generateAciIdentityKeyIfNecessary()
|
||||
val aciIdentity: IdentityKeyPair = SignalStore.account().aciIdentityKey
|
||||
|
||||
SignalStore.account().generatePniIdentityKeyIfNecessary()
|
||||
val pniIdentity: IdentityKeyPair = SignalStore.account().pniIdentityKey
|
||||
|
||||
val aciPreKeyCollection = RegistrationRepository.generateSignedAndLastResortPreKeys(aciIdentity, SignalStore.account().aciPreKeys)
|
||||
val pniPreKeyCollection = RegistrationRepository.generateSignedAndLastResortPreKeys(pniIdentity, SignalStore.account().pniPreKeys)
|
||||
|
||||
return Single.fromCallable {
|
||||
val response = accountManager.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, registrationData.aciPreKeyCollection, registrationData.pniPreKeyCollection, registrationData.fcmToken, true)
|
||||
VerifyResponse.from(response, kbsData, pin)
|
||||
val response = accountManager.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, aciPreKeyCollection, pniPreKeyCollection, registrationData.fcmToken, true)
|
||||
VerifyResponse.from(response, kbsData, pin, aciPreKeyCollection, pniPreKeyCollection)
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,27 @@
|
|||
package org.thoughtcrime.securesms.registration
|
||||
|
||||
import org.whispersystems.signalservice.api.KbsPinData
|
||||
import org.whispersystems.signalservice.api.account.PreKeyCollection
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
|
||||
|
||||
data class VerifyResponse(val verifyAccountResponse: VerifyAccountResponse, val kbsData: KbsPinData?, val pin: String?) {
|
||||
data class VerifyResponse(
|
||||
val verifyAccountResponse: VerifyAccountResponse,
|
||||
val kbsData: KbsPinData?,
|
||||
val pin: String?,
|
||||
val aciPreKeyCollection: PreKeyCollection?,
|
||||
val pniPreKeyCollection: PreKeyCollection?
|
||||
) {
|
||||
companion object {
|
||||
fun from(response: ServiceResponse<VerifyAccountResponse>, kbsData: KbsPinData?, pin: String?): ServiceResponse<VerifyResponse> {
|
||||
fun from(
|
||||
response: ServiceResponse<VerifyAccountResponse>,
|
||||
kbsData: KbsPinData?,
|
||||
pin: String?,
|
||||
aciPreKeyCollection: PreKeyCollection?,
|
||||
pniPreKeyCollection: PreKeyCollection?
|
||||
): ServiceResponse<VerifyResponse> {
|
||||
return if (response.result.isPresent) {
|
||||
ServiceResponse.forResult(VerifyResponse(response.result.get(), kbsData, pin), 200, null)
|
||||
ServiceResponse.forResult(VerifyResponse(response.result.get(), kbsData, pin, aciPreKeyCollection, pniPreKeyCollection), 200, null)
|
||||
} else {
|
||||
ServiceResponse.coerceError(response)
|
||||
}
|
||||
|
|
|
@ -236,8 +236,6 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
|||
getRegistrationSecret(),
|
||||
registrationRepository.getRegistrationId(),
|
||||
registrationRepository.getProfileKey(getNumber().getE164Number()),
|
||||
RegistrationRepository.generatePreKeysForType(ServiceIdType.ACI),
|
||||
RegistrationRepository.generatePreKeysForType(ServiceIdType.PNI),
|
||||
getFcmToken(),
|
||||
registrationRepository.getPniRegistrationId(),
|
||||
getSessionId() != null ? null : getRecoveryPassword());
|
||||
|
@ -334,7 +332,7 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
|||
boolean setRegistrationLockEnabled = verifyResponse.getKbsData() != null;
|
||||
|
||||
if (!setRegistrationLockEnabled) {
|
||||
verifyResponse = new VerifyResponse(processor.getResult().getVerifyAccountResponse(), pinData, pin);
|
||||
verifyResponse = new VerifyResponse(processor.getResult().getVerifyAccountResponse(), pinData, pin, verifyResponse.getAciPreKeyCollection(), verifyResponse.getPniPreKeyCollection());
|
||||
}
|
||||
|
||||
return registrationRepository.registerAccount(registrationData, verifyResponse, setRegistrationLockEnabled)
|
||||
|
|
|
@ -6,27 +6,16 @@
|
|||
package org.whispersystems.signalservice.api.account
|
||||
|
||||
import org.signal.libsignal.protocol.IdentityKey
|
||||
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.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
|
||||
* 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 identityKeyPair: IdentityKeyPair,
|
||||
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>
|
||||
val lastResortKyberPreKey: KyberPreKeyRecord
|
||||
)
|
||||
|
|
|
@ -433,7 +433,8 @@ public class PushServiceSocket {
|
|||
aciLastResortKyberPreKey,
|
||||
pniLastResortKyberPreKey,
|
||||
gcmRegistrationId,
|
||||
skipDeviceTransfer);
|
||||
skipDeviceTransfer,
|
||||
true);
|
||||
|
||||
String response = makeServiceRequest(path, "POST", JsonUtil.toJson(body), NO_HEADERS, new RegistrationSessionResponseHandler(), Optional.empty());
|
||||
return JsonUtil.fromJson(response, VerifyAccountResponse.class);
|
||||
|
|
|
@ -17,5 +17,6 @@ data class RegistrationSessionRequestBody(
|
|||
@JsonProperty val aciPqLastResortPreKey: KyberPreKeyEntity,
|
||||
@JsonProperty val pniPqLastResortPreKey: KyberPreKeyEntity,
|
||||
@JsonProperty val gcmToken: GcmRegistrationId?,
|
||||
@JsonProperty val skipDeviceTransfer: Boolean
|
||||
@JsonProperty val skipDeviceTransfer: Boolean,
|
||||
@JsonProperty val requireAtomic: Boolean = true
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue