Update libsignal-service-java

This commit is contained in:
AsamK 2022-02-20 12:22:37 +01:00
parent 2c44b65e9f
commit f3b2df62da
21 changed files with 273 additions and 120 deletions

View file

@ -12,6 +12,10 @@
"name":"java.lang.Boolean",
"methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]
},
{
"name":"java.lang.Class",
"methods":[{"name":"getCanonicalName","parameterTypes":[] }]
},
{
"name":"java.lang.ClassLoader",
"methods":[
@ -30,6 +34,10 @@
{
"name":"java.lang.NoSuchMethodError"
},
{
"name":"java.lang.Throwable",
"methods":[{"name":"getMessage","parameterTypes":[] }]
},
{
"name":"java.lang.UnsatisfiedLinkError",
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
@ -115,6 +123,9 @@
"name":"org.whispersystems.libsignal.InvalidKeyException",
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
},
{
"name":"org.whispersystems.libsignal.InvalidKeyIdException"
},
{
"name":"org.whispersystems.libsignal.InvalidMessageException",
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]

View file

@ -2023,11 +2023,6 @@
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"org.whispersystems.signalservice.api.push.AccountIdentifier",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true
},
{
"name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity",
"allDeclaredFields":true,
@ -2314,16 +2309,19 @@
{
"name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisionMessage",
"fields":[
{"name":"aciIdentityKeyPrivate_"},
{"name":"aciIdentityKeyPublic_"},
{"name":"aci_"},
{"name":"bitField0_"},
{"name":"identityKeyPrivate_"},
{"name":"identityKeyPublic_"},
{"name":"number_"},
{"name":"pniIdentityKeyPrivate_"},
{"name":"pniIdentityKeyPublic_"},
{"name":"pni_"},
{"name":"profileKey_"},
{"name":"provisioningCode_"},
{"name":"provisioningVersion_"},
{"name":"readReceipts_"},
{"name":"userAgent_"},
{"name":"uuid_"}
{"name":"userAgent_"}
]
},
{
@ -2686,6 +2684,7 @@
"fields":[
{"name":"bitField0_"},
{"name":"content_"},
{"name":"destinationUuid_"},
{"name":"legacyMessage_"},
{"name":"relay_"},
{"name":"serverGuid_"},
@ -2852,6 +2851,14 @@
{"name":"type_"}
]
},
{
"name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$PniIdentity",
"fields":[
{"name":"bitField0_"},
{"name":"privateKey_"},
{"name":"publicKey_"}
]
},
{
"name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Read",
"fields":[

View file

@ -14,7 +14,7 @@ repositories {
}
dependencies {
implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_41")
implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_42")
implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.1")
implementation("com.google.protobuf", "protobuf-javalite", "3.11.4")
implementation("org.bouncycastle", "bcprov-jdk15on", "1.70")

View file

@ -278,7 +278,7 @@ class ManagerImpl implements Manager {
public List<Device> getLinkedDevices() throws IOException {
var devices = dependencies.getAccountManager().getDevices();
account.setMultiDevice(devices.size() > 1);
var identityKey = account.getIdentityKeyPair().getPrivateKey();
var identityKey = account.getAciIdentityKeyPair().getPrivateKey();
return devices.stream().map(d -> {
String deviceName = d.getName();
if (deviceName != null) {
@ -568,7 +568,7 @@ class ManagerImpl implements Manager {
final var recipientId = context.getRecipientHelper().resolveRecipient(m.recipient());
mentions.add(new SignalServiceDataMessage.Mention(context.getRecipientHelper()
.resolveSignalServiceAddress(recipientId)
.getAci(), m.start(), m.length()));
.getServiceId(), m.start(), m.length()));
}
return mentions;
}

View file

@ -79,7 +79,7 @@ class ProvisioningManagerImpl implements ProvisioningManager {
groupsV2Operations = null;
}
accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
new DynamicCredentialsProvider(null, null, password, SignalServiceAddress.DEFAULT_DEVICE_ID),
new DynamicCredentialsProvider(null, null, null, password, SignalServiceAddress.DEFAULT_DEVICE_ID),
userAgent,
groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY);
@ -97,6 +97,7 @@ class ProvisioningManagerImpl implements ProvisioningManager {
var ret = accountManager.getNewDeviceRegistration(tempIdentityKey);
var number = ret.getNumber();
var aci = ret.getAci();
var pni = ret.getPni();
logger.info("Received link information from {}, linking in progress ...", number);
@ -117,7 +118,7 @@ class ProvisioningManagerImpl implements ProvisioningManager {
var encryptedDeviceName = deviceName == null
? null
: DeviceNameUtil.encryptDeviceName(deviceName, ret.getIdentity().getPrivateKey());
: DeviceNameUtil.encryptDeviceName(deviceName, ret.getAciIdentity().getPrivateKey());
logger.debug("Finishing new device registration");
var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(),
@ -135,10 +136,12 @@ class ProvisioningManagerImpl implements ProvisioningManager {
accountPath,
number,
aci,
pni,
password,
encryptedDeviceName,
deviceId,
ret.getIdentity(),
ret.getAciIdentity(),
ret.getPniIdentity(),
registrationId,
profileKey,
TrustNewIdentity.ON_FIRST_USE);

View file

@ -31,6 +31,7 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.PNI;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.internal.ServiceResponse;
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse;
@ -79,7 +80,7 @@ class RegistrationManagerImpl implements RegistrationManager {
this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
new DynamicCredentialsProvider(
// Using empty UUID, because registering doesn't work otherwise
null, account.getNumber(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID),
null, null, account.getNumber(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID),
userAgent,
groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY);
@ -116,7 +117,8 @@ class RegistrationManagerImpl implements RegistrationManager {
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
final var aci = ACI.parseOrNull(response.getUuid());
account.finishRegistration(aci, masterKey, pin);
final var pni = PNI.parseOrNull(response.getPni());
account.finishRegistration(aci, pni, masterKey, pin);
accountFileUpdater.updateAccountIdentifiers(account.getNumber(), aci);
ManagerImpl m = null;

View file

@ -125,14 +125,16 @@ public class SignalAccountFiles {
final var accountPath = accountsStore.getPathByNumber(number);
if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) {
final var newAccountPath = accountPath == null ? accountsStore.addAccount(number, null) : accountPath;
var identityKey = KeyUtils.generateIdentityKeyPair();
var aciIdentityKey = KeyUtils.generateIdentityKeyPair();
var pniIdentityKey = KeyUtils.generateIdentityKeyPair();
var registrationId = KeyHelper.generateRegistrationId(false);
var profileKey = KeyUtils.createProfileKey();
var account = SignalAccount.create(pathConfig.dataPath(),
newAccountPath,
number,
identityKey,
aciIdentityKey,
pniIdentityKey,
registrationId,
profileKey,
trustNewIdentity);

View file

@ -88,6 +88,7 @@ public class SignalDependencies {
public SignalServiceAccountManager createUnauthenticatedAccountManager(String number, String password) {
return new SignalServiceAccountManager(getServiceEnvironmentConfig().getSignalServiceConfiguration(),
null,
null,
number,
SignalServiceAddress.DEFAULT_DEVICE_ID,

View file

@ -17,10 +17,8 @@ public class RetrieveStorageDataAction implements HandleAction {
public void execute(Context context) throws Throwable {
if (context.getAccount().getStorageKey() != null) {
context.getStorageHelper().readDataFromStorage();
} else {
if (!context.getAccount().isMasterDevice()) {
context.getSyncHelper().requestAllSyncData();
}
} else if (!context.getAccount().isMasterDevice()) {
context.getSyncHelper().requestSyncKeys();
}
}
}

View file

@ -0,0 +1,20 @@
package org.asamk.signal.manager.actions;
import org.asamk.signal.manager.helper.Context;
public class SendPniIdentityKeyAction implements HandleAction {
private static final SendPniIdentityKeyAction INSTANCE = new SendPniIdentityKeyAction();
private SendPniIdentityKeyAction() {
}
public static SendPniIdentityKeyAction create() {
return INSTANCE;
}
@Override
public void execute(Context context) throws Throwable {
context.getSyncHelper().sendPniIdentity();
}
}

View file

@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.PNI;
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
import org.whispersystems.signalservice.api.util.DeviceNameUtil;
@ -55,9 +56,12 @@ public class AccountHelper {
}
try {
context.getPreKeyHelper().refreshPreKeysIfNecessary();
if (account.getAci() == null) {
if (account.getAci() == null || account.getPni() == null) {
checkWhoAmiI();
}
if (!account.isMasterDevice() && account.getPniIdentityKeyPair() == null) {
context.getSyncHelper().requestSyncPniIdentity();
}
updateAccountAttributes();
} catch (AuthorizationFailedException e) {
account.setRegistered(false);
@ -69,16 +73,18 @@ public class AccountHelper {
final var whoAmI = dependencies.getAccountManager().getWhoAmI();
final var number = whoAmI.getNumber();
final var aci = ACI.parseOrNull(whoAmI.getAci());
if (number.equals(account.getNumber()) && aci.equals(account.getAci())) {
final var pni = PNI.parseOrNull(whoAmI.getPni());
if (number.equals(account.getNumber()) && aci.equals(account.getAci()) && pni.equals(account.getPni())) {
return;
}
updateSelfIdentifiers(number, aci);
updateSelfIdentifiers(number, aci, pni);
}
private void updateSelfIdentifiers(final String number, final ACI aci) {
private void updateSelfIdentifiers(final String number, final ACI aci, final PNI pni) {
account.setNumber(number);
account.setAci(aci);
account.setPni(pni);
account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress());
// TODO check and update remote storage
context.getUnidentifiedAccessHelper().rotateSenderCertificates();
@ -103,11 +109,11 @@ public class AccountHelper {
(verificationCode1, registrationLock) -> dependencies.getAccountManager()
.changeNumber(verificationCode1, newNumber, registrationLock));
// TODO handle response
updateSelfIdentifiers(newNumber, account.getAci());
updateSelfIdentifiers(newNumber, account.getAci(), PNI.parseOrThrow(result.first().getPni()));
}
public void setDeviceName(String deviceName) {
final var privateKey = account.getIdentityKeyPair().getPrivateKey();
final var privateKey = account.getAciIdentityKeyPair().getPrivateKey();
final var encryptedDeviceName = DeviceNameUtil.encryptDeviceName(deviceName, privateKey);
account.setEncryptedDeviceName(encryptedDeviceName);
}
@ -127,15 +133,15 @@ public class AccountHelper {
}
public void addDevice(DeviceLinkInfo deviceLinkInfo) throws IOException, InvalidDeviceLinkException {
var identityKeyPair = account.getIdentityKeyPair();
var verificationCode = dependencies.getAccountManager().getNewDeviceVerificationCode();
try {
dependencies.getAccountManager()
.addDevice(deviceLinkInfo.deviceIdentifier(),
deviceLinkInfo.deviceKey(),
identityKeyPair,
Optional.of(account.getProfileKey().serialize()),
account.getAciIdentityKeyPair(),
account.getPniIdentityKeyPair(),
account.getProfileKey(),
verificationCode);
} catch (InvalidKeyException e) {
throw new InvalidDeviceLinkException("Invalid device link", e);

View file

@ -38,6 +38,7 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException;
import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
@ -150,7 +151,7 @@ class GroupV2Helper {
final var memberList = new ArrayList<>(members);
final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream();
final var uuids = memberList.stream()
.map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getAci().uuid());
.map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid());
var candidates = Utils.zip(uuids,
credentials,
(uuid, credential) -> new GroupCandidate(uuid, Optional.fromNullable(credential)))
@ -218,7 +219,7 @@ class GroupV2Helper {
final var memberList = new ArrayList<>(newMembers);
final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream();
final var uuids = memberList.stream()
.map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getAci().uuid());
.map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid());
var candidates = Utils.zip(uuids,
credentials,
(uuid, credential) -> new GroupCandidate(uuid, Optional.fromNullable(credential)))
@ -245,8 +246,8 @@ class GroupV2Helper {
final var adminUuids = membersToMakeAdmin.stream()
.map(context.getRecipientHelper()::resolveSignalServiceAddress)
.map(SignalServiceAddress::getAci)
.map(ACI::uuid)
.map(SignalServiceAddress::getServiceId)
.map(ServiceId::uuid)
.toList();
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
return commitChange(groupInfoV2,
@ -258,8 +259,8 @@ class GroupV2Helper {
) throws IOException {
final var memberUuids = members.stream()
.map(context.getRecipientHelper()::resolveSignalServiceAddress)
.map(SignalServiceAddress::getAci)
.map(ACI::uuid)
.map(SignalServiceAddress::getServiceId)
.map(ServiceId::uuid)
.collect(Collectors.toSet());
return ejectMembers(groupInfoV2, memberUuids);
}
@ -270,8 +271,8 @@ class GroupV2Helper {
var pendingMembersList = groupInfoV2.getGroup().getPendingMembersList();
final var memberUuids = members.stream()
.map(context.getRecipientHelper()::resolveSignalServiceAddress)
.map(SignalServiceAddress::getAci)
.map(ACI::uuid)
.map(SignalServiceAddress::getServiceId)
.map(ServiceId::uuid)
.map(uuid -> DecryptedGroupUtil.findPendingByUuid(pendingMembersList, uuid))
.filter(Optional::isPresent)
.map(Optional::get)
@ -343,7 +344,7 @@ class GroupV2Helper {
change.setSourceUuid(context.getRecipientHelper()
.resolveSignalServiceAddress(selfRecipientId)
.getAci()
.getServiceId()
.toByteString());
return commitChange(groupSecretParams, decryptedGroupJoinInfo.getRevision(), change, groupLinkPassword);
@ -360,7 +361,7 @@ class GroupV2Helper {
final var change = groupOperations.createAcceptInviteChange(profileKeyCredential);
final var aci = context.getRecipientHelper().resolveSignalServiceAddress(selfRecipientId).getAci();
final var aci = context.getRecipientHelper().resolveSignalServiceAddress(selfRecipientId).getServiceId();
change.setSourceUuid(aci.toByteString());
return commitChange(groupInfoV2, change);
@ -372,7 +373,7 @@ class GroupV2Helper {
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
final var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId);
final var newRole = admin ? Member.Role.ADMINISTRATOR : Member.Role.DEFAULT;
final var change = groupOperations.createChangeMemberRole(address.getAci().uuid(), newRole);
final var change = groupOperations.createChangeMemberRole(address.getServiceId().uuid(), newRole);
return commitChange(groupInfoV2, change);
}

View file

@ -77,7 +77,7 @@ public class IdentityHelper {
) {
return Utils.computeSafetyNumber(capabilities.isUuid(),
account.getSelfAddress(),
account.getIdentityKeyPair().getPublicKey(),
account.getAciIdentityKeyPair().getPublicKey(),
theirAddress,
theirIdentityKey);
}

View file

@ -10,6 +10,7 @@ import org.asamk.signal.manager.actions.RetrieveProfileAction;
import org.asamk.signal.manager.actions.RetrieveStorageDataAction;
import org.asamk.signal.manager.actions.SendGroupInfoAction;
import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
import org.asamk.signal.manager.actions.SendPniIdentityKeyAction;
import org.asamk.signal.manager.actions.SendReceiptAction;
import org.asamk.signal.manager.actions.SendRetryMessageRequestAction;
import org.asamk.signal.manager.actions.SendSyncBlockedListAction;
@ -31,6 +32,7 @@ import org.asamk.signal.manager.storage.groups.GroupInfoV1;
import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.storage.stickers.Sticker;
import org.asamk.signal.manager.util.KeyUtils;
import org.signal.libsignal.metadata.ProtocolInvalidKeyException;
import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException;
import org.signal.libsignal.metadata.ProtocolInvalidMessageException;
@ -340,6 +342,9 @@ public final class IncomingMessageHandler {
if (rm.isConfigurationRequest()) {
actions.add(SendSyncConfigurationAction.create());
}
if (rm.isPniIdentityRequest()) {
actions.add(SendPniIdentityKeyAction.create());
}
}
if (syncMessage.getGroups().isPresent()) {
logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring.");
@ -440,6 +445,11 @@ public final class IncomingMessageHandler {
.get());
}
}
if (syncMessage.getPniIdentity().isPresent()) {
final var pniIdentity = syncMessage.getPniIdentity().get();
account.setPniIdentityKeyPair(KeyUtils.getIdentityKeyPair(pniIdentity.getPublicKey().toByteArray(),
pniIdentity.getPrivateKey().toByteArray()));
}
return actions;
}

View file

@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.state.PreKeyRecord;
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
import org.whispersystems.signalservice.api.push.ServiceIdType;
import java.io.IOException;
import java.util.List;
@ -28,17 +29,32 @@ public class PreKeyHelper {
}
public void refreshPreKeysIfNecessary() throws IOException {
if (dependencies.getAccountManager().getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) {
refreshPreKeys();
refreshPreKeysIfNecessary(ServiceIdType.ACI);
refreshPreKeysIfNecessary(ServiceIdType.PNI);
}
public void refreshPreKeysIfNecessary(ServiceIdType serviceIdType) throws IOException {
if (dependencies.getAccountManager().getPreKeysCount(serviceIdType) < ServiceConfig.PREKEY_MINIMUM_COUNT) {
refreshPreKeys(serviceIdType);
}
}
public void refreshPreKeys() throws IOException {
refreshPreKeys(ServiceIdType.ACI);
refreshPreKeys(ServiceIdType.PNI);
}
public void refreshPreKeys(ServiceIdType serviceIdType) throws IOException {
if (serviceIdType != ServiceIdType.ACI) {
// TODO implement
return;
}
var oneTimePreKeys = generatePreKeys();
final var identityKeyPair = account.getIdentityKeyPair();
final var identityKeyPair = account.getAciIdentityKeyPair();
var signedPreKeyRecord = generateSignedPreKey(identityKeyPair);
dependencies.getAccountManager().setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys);
dependencies.getAccountManager()
.setPreKeys(serviceIdType, identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys);
}
private List<PreKeyRecord> generatePreKeys() {

View file

@ -1,5 +1,7 @@
package org.asamk.signal.manager.helper;
import com.google.protobuf.ByteString;
import org.asamk.signal.manager.api.TrustLevel;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.groups.GroupInfoV1;
@ -49,11 +51,20 @@ public class SyncHelper {
}
public void requestAllSyncData() {
requestSyncGroups();
requestSyncContacts();
requestSyncBlocked();
requestSyncConfiguration();
requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.GROUPS);
requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS);
requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED);
requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION);
requestSyncKeys();
requestSyncPniIdentity();
}
public void requestSyncKeys() {
requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.KEYS);
}
public void requestSyncPniIdentity() {
requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.PNI_IDENTITY);
}
public void sendSyncFetchProfileMessage() {
@ -217,6 +228,15 @@ public class SyncHelper {
context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forConfiguration(configurationMessage));
}
public void sendPniIdentity() {
final var pniIdentityKeyPair = account.getPniIdentityKeyPair();
var pniIdentity = SignalServiceProtos.SyncMessage.PniIdentity.newBuilder()
.setPrivateKey(ByteString.copyFrom(pniIdentityKeyPair.getPrivateKey().serialize()))
.setPublicKey(ByteString.copyFrom(pniIdentityKeyPair.getPublicKey().serialize()))
.build();
context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forPniIdentity(pniIdentity));
}
public void handleSyncDeviceContacts(final InputStream input) throws IOException {
final var s = new DeviceContactsInputStream(input);
DeviceContact c;
@ -270,42 +290,8 @@ public class SyncHelper {
}
}
private void requestSyncGroups() {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
.setType(SignalServiceProtos.SyncMessage.Request.Type.GROUPS)
.build();
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
context.getSendHelper().sendSyncMessage(message);
}
private void requestSyncContacts() {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
.setType(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS)
.build();
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
context.getSendHelper().sendSyncMessage(message);
}
private void requestSyncBlocked() {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
.setType(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED)
.build();
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
context.getSendHelper().sendSyncMessage(message);
}
private void requestSyncConfiguration() {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
.setType(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION)
.build();
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
context.getSendHelper().sendSyncMessage(message);
}
private void requestSyncKeys() {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
.setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS)
.build();
private void requestSyncData(final SignalServiceProtos.SyncMessage.Request.Type type) {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder().setType(type).build();
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
context.getSendHelper().sendSyncMessage(message);
}

View file

@ -49,8 +49,9 @@ import org.whispersystems.signalservice.api.SignalServiceDataStore;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
import org.whispersystems.signalservice.api.kbs.MasterKey;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.AccountIdentifier;
import org.whispersystems.signalservice.api.push.DistributionId;
import org.whispersystems.signalservice.api.push.PNI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.storage.StorageKey;
import org.whispersystems.signalservice.api.util.CredentialsProvider;
@ -94,6 +95,7 @@ public class SignalAccount implements Closeable {
private String accountPath;
private String number;
private ACI aci;
private PNI pni;
private String encryptedDeviceName;
private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
private boolean isMultiDevice = false;
@ -105,7 +107,8 @@ public class SignalAccount implements Closeable {
private ProfileKey profileKey;
private int preKeyIdOffset = 1;
private int nextSignedPreKeyId = 1;
private IdentityKeyPair identityKeyPair;
private IdentityKeyPair aciIdentityKeyPair;
private IdentityKeyPair pniIdentityKeyPair;
private int localRegistrationId;
private TrustNewIdentity trustNewIdentity;
private long lastReceiveTimestamp = 0;
@ -161,7 +164,8 @@ public class SignalAccount implements Closeable {
File dataPath,
String accountPath,
String number,
IdentityKeyPair identityKey,
IdentityKeyPair aciIdentityKey,
IdentityKeyPair pniIdentityKey,
int registrationId,
ProfileKey profileKey,
final TrustNewIdentity trustNewIdentity
@ -180,7 +184,8 @@ public class SignalAccount implements Closeable {
signalAccount.profileKey = profileKey;
signalAccount.dataPath = dataPath;
signalAccount.identityKeyPair = identityKey;
signalAccount.aciIdentityKeyPair = aciIdentityKey;
signalAccount.pniIdentityKeyPair = pniIdentityKey;
signalAccount.localRegistrationId = registrationId;
signalAccount.trustNewIdentity = trustNewIdentity;
signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath),
@ -203,10 +208,12 @@ public class SignalAccount implements Closeable {
String accountPath,
String number,
ACI aci,
PNI pni,
String password,
String encryptedDeviceName,
int deviceId,
IdentityKeyPair identityKey,
IdentityKeyPair aciIdentityKey,
IdentityKeyPair pniIdentityKey,
int registrationId,
ProfileKey profileKey,
final TrustNewIdentity trustNewIdentity
@ -218,17 +225,27 @@ public class SignalAccount implements Closeable {
accountPath,
number,
aci,
pni,
password,
encryptedDeviceName,
deviceId,
identityKey,
aciIdentityKey,
pniIdentityKey,
registrationId,
profileKey,
trustNewIdentity);
}
final var signalAccount = load(dataPath, accountPath, true, trustNewIdentity);
signalAccount.setProvisioningData(number, aci, password, encryptedDeviceName, deviceId, profileKey);
signalAccount.setProvisioningData(number,
aci,
pni,
password,
encryptedDeviceName,
deviceId,
aciIdentityKey,
pniIdentityKey,
profileKey);
signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress());
signalAccount.getSessionStore().archiveAllSessions();
signalAccount.getSenderKeyStore().deleteAll();
@ -253,10 +270,12 @@ public class SignalAccount implements Closeable {
String accountPath,
String number,
ACI aci,
PNI pni,
String password,
String encryptedDeviceName,
int deviceId,
IdentityKeyPair identityKey,
IdentityKeyPair aciIdentityKey,
IdentityKeyPair pniIdentityKey,
int registrationId,
ProfileKey profileKey,
final TrustNewIdentity trustNewIdentity
@ -267,11 +286,18 @@ public class SignalAccount implements Closeable {
final var pair = openFileChannel(fileName, true);
var signalAccount = new SignalAccount(pair.first(), pair.second());
signalAccount.setProvisioningData(number, aci, password, encryptedDeviceName, deviceId, profileKey);
signalAccount.setProvisioningData(number,
aci,
pni,
password,
encryptedDeviceName,
deviceId,
aciIdentityKey,
pniIdentityKey,
profileKey);
signalAccount.dataPath = dataPath;
signalAccount.accountPath = accountPath;
signalAccount.identityKeyPair = identityKey;
signalAccount.localRegistrationId = registrationId;
signalAccount.trustNewIdentity = trustNewIdentity;
signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath),
@ -283,6 +309,7 @@ public class SignalAccount implements Closeable {
signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress());
signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION;
signalAccount.migrateLegacyConfigs();
signalAccount.clearAllPreKeys();
signalAccount.save();
return signalAccount;
@ -291,17 +318,23 @@ public class SignalAccount implements Closeable {
private void setProvisioningData(
final String number,
final ACI aci,
final PNI pni,
final String password,
final String encryptedDeviceName,
final int deviceId,
final IdentityKeyPair aciIdentity,
final IdentityKeyPair pniIdentity,
final ProfileKey profileKey
) {
this.number = number;
this.aci = aci;
this.pni = pni;
this.password = password;
this.profileKey = profileKey;
this.encryptedDeviceName = encryptedDeviceName;
this.deviceId = deviceId;
this.aciIdentityKeyPair = aciIdentity;
this.pniIdentityKeyPair = pniIdentity;
this.registered = true;
this.isMultiDevice = true;
this.lastReceiveTimestamp = 0;
@ -330,6 +363,9 @@ public class SignalAccount implements Closeable {
}
save();
}
if (isMasterDevice() && getPniIdentityKeyPair() == null) {
setPniIdentityKeyPair(KeyUtils.generateIdentityKeyPair());
}
}
private void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) {
@ -440,7 +476,14 @@ public class SignalAccount implements Closeable {
try {
aci = ACI.parseOrThrow(rootNode.get("uuid").asText());
} catch (IllegalArgumentException e) {
throw new IOException("Config file contains an invalid uuid, needs to be a valid UUID", e);
throw new IOException("Config file contains an invalid aci/uuid, needs to be a valid UUID", e);
}
}
if (rootNode.hasNonNull("pni")) {
try {
pni = PNI.parseOrThrow(rootNode.get("pni").asText());
} catch (IllegalArgumentException e) {
throw new IOException("Config file contains an invalid pni, needs to be a valid UUID", e);
}
}
if (rootNode.hasNonNull("deviceName")) {
@ -459,11 +502,16 @@ public class SignalAccount implements Closeable {
if (rootNode.hasNonNull("registrationId")) {
registrationId = rootNode.get("registrationId").asInt();
}
IdentityKeyPair identityKeyPair = null;
IdentityKeyPair aciIdentityKeyPair = null;
if (rootNode.hasNonNull("identityPrivateKey") && rootNode.hasNonNull("identityKey")) {
final var publicKeyBytes = Base64.getDecoder().decode(rootNode.get("identityKey").asText());
final var privateKeyBytes = Base64.getDecoder().decode(rootNode.get("identityPrivateKey").asText());
identityKeyPair = KeyUtils.getIdentityKeyPair(publicKeyBytes, privateKeyBytes);
aciIdentityKeyPair = KeyUtils.getIdentityKeyPair(publicKeyBytes, privateKeyBytes);
}
if (rootNode.hasNonNull("pniIdentityPrivateKey") && rootNode.hasNonNull("pniIdentityKey")) {
final var publicKeyBytes = Base64.getDecoder().decode(rootNode.get("pniIdentityKey").asText());
final var privateKeyBytes = Base64.getDecoder().decode(rootNode.get("pniIdentityPrivateKey").asText());
pniIdentityKeyPair = KeyUtils.getIdentityKeyPair(publicKeyBytes, privateKeyBytes);
}
if (rootNode.hasNonNull("registrationLockPin")) {
@ -504,12 +552,12 @@ public class SignalAccount implements Closeable {
LegacyJsonSignalProtocolStore.class)
: null;
if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) {
identityKeyPair = legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentityKeyPair();
aciIdentityKeyPair = legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentityKeyPair();
registrationId = legacySignalProtocolStore.getLegacyIdentityKeyStore().getLocalRegistrationId();
migratedLegacyConfig = true;
}
this.identityKeyPair = identityKeyPair;
this.aciIdentityKeyPair = aciIdentityKeyPair;
this.localRegistrationId = registrationId;
this.trustNewIdentity = trustNewIdentity;
@ -741,6 +789,7 @@ public class SignalAccount implements Closeable {
rootNode.put("version", CURRENT_STORAGE_VERSION)
.put("username", number)
.put("uuid", aci == null ? null : aci.toString())
.put("pni", pni == null ? null : pni.toString())
.put("deviceName", encryptedDeviceName)
.put("deviceId", deviceId)
.put("isMultiDevice", isMultiDevice)
@ -748,8 +797,18 @@ public class SignalAccount implements Closeable {
.put("password", password)
.put("registrationId", localRegistrationId)
.put("identityPrivateKey",
Base64.getEncoder().encodeToString(identityKeyPair.getPrivateKey().serialize()))
.put("identityKey", Base64.getEncoder().encodeToString(identityKeyPair.getPublicKey().serialize()))
Base64.getEncoder().encodeToString(aciIdentityKeyPair.getPrivateKey().serialize()))
.put("identityKey",
Base64.getEncoder().encodeToString(aciIdentityKeyPair.getPublicKey().serialize()))
.put("pniIdentityPrivateKey",
pniIdentityKeyPair == null
? null
: Base64.getEncoder()
.encodeToString(pniIdentityKeyPair.getPrivateKey().serialize()))
.put("pniIdentityKey",
pniIdentityKeyPair == null
? null
: Base64.getEncoder().encodeToString(pniIdentityKeyPair.getPublicKey().serialize()))
.put("registrationLockPin", registrationLockPin)
.put("pinMasterKey",
pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize()))
@ -820,8 +879,14 @@ public class SignalAccount implements Closeable {
public SignalServiceDataStore getSignalServiceDataStore() {
return new SignalServiceDataStore() {
@Override
public SignalServiceAccountDataStore get(final AccountIdentifier accountIdentifier) {
public SignalServiceAccountDataStore get(final ServiceId accountIdentifier) {
if (accountIdentifier.equals(aci)) {
return getSignalServiceAccountDataStore();
} else if (accountIdentifier.equals(pni)) {
throw new AssertionError("PNI not to be used yet!");
} else {
throw new IllegalArgumentException("No matching store found for " + accountIdentifier);
}
}
@Override
@ -831,7 +896,7 @@ public class SignalAccount implements Closeable {
@Override
public SignalServiceAccountDataStore pni() {
return getSignalServiceAccountDataStore();
throw new AssertionError("PNI not to be used yet!");
}
@Override
@ -870,7 +935,7 @@ public class SignalAccount implements Closeable {
return getOrCreate(() -> identityKeyStore,
() -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, accountPath),
getRecipientStore(),
identityKeyPair,
aciIdentityKeyPair,
localRegistrationId,
trustNewIdentity));
}
@ -937,6 +1002,11 @@ public class SignalAccount implements Closeable {
return aci;
}
@Override
public PNI getPni() {
return pni;
}
@Override
public String getE164() {
return number;
@ -972,6 +1042,15 @@ public class SignalAccount implements Closeable {
save();
}
public PNI getPni() {
return pni;
}
public void setPni(final PNI pni) {
this.pni = pni;
save();
}
public SignalServiceAddress getSelfAddress() {
return new SignalServiceAddress(aci, number);
}
@ -1001,8 +1080,17 @@ public class SignalAccount implements Closeable {
return deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID;
}
public IdentityKeyPair getIdentityKeyPair() {
return identityKeyPair;
public IdentityKeyPair getAciIdentityKeyPair() {
return aciIdentityKeyPair;
}
public IdentityKeyPair getPniIdentityKeyPair() {
return pniIdentityKeyPair;
}
public void setPniIdentityKeyPair(final IdentityKeyPair identityKeyPair) {
pniIdentityKeyPair = identityKeyPair;
save();
}
public int getLocalRegistrationId() {
@ -1118,7 +1206,7 @@ public class SignalAccount implements Closeable {
return configurationStore.getPhoneNumberUnlisted() == null || !configurationStore.getPhoneNumberUnlisted();
}
public void finishRegistration(final ACI aci, final MasterKey masterKey, final String pin) {
public void finishRegistration(final ACI aci, final PNI pni, final MasterKey masterKey, final String pin) {
this.pinMasterKey = masterKey;
this.storageManifestVersion = -1;
this.storageKey = null;
@ -1127,6 +1215,7 @@ public class SignalAccount implements Closeable {
this.isMultiDevice = false;
this.registered = true;
this.aci = aci;
this.pni = pni;
this.registrationLockPin = pin;
this.lastReceiveTimestamp = 0;
save();
@ -1135,7 +1224,7 @@ public class SignalAccount implements Closeable {
getSessionStore().archiveAllSessions();
getSenderKeyStore().deleteAll();
final var recipientId = getRecipientStore().resolveSelfRecipientTrusted(getSelfRecipientAddress());
final var publicKey = getIdentityKeyPair().getPublicKey();
final var publicKey = getAciIdentityKeyPair().getPublicKey();
getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date());
getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED);
}

View file

@ -28,7 +28,7 @@ public record RecipientAddress(Optional<UUID> uuid, Optional<String> number) {
}
public RecipientAddress(SignalServiceAddress address) {
this(Optional.of(address.getAci().uuid()), Optional.ofNullable(address.getNumber().orNull()));
this(Optional.of(address.getServiceId().uuid()), Optional.ofNullable(address.getNumber().orNull()));
}
public RecipientAddress(UUID uuid) {

View file

@ -1,6 +1,6 @@
package org.asamk.signal.manager.storage.recipients;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
public interface RecipientResolver {
@ -11,7 +11,7 @@ public interface RecipientResolver {
RecipientId resolveRecipient(SignalServiceAddress address);
RecipientId resolveRecipient(ACI aci);
RecipientId resolveRecipient(ServiceId aci);
RecipientId resolveRecipient(long recipientId);
}

View file

@ -13,6 +13,7 @@ import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
@ -171,8 +172,8 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile
}
@Override
public RecipientId resolveRecipient(ACI aci) {
return resolveRecipient(new RecipientAddress(aci.uuid()), false, false);
public RecipientId resolveRecipient(ServiceId serviceId) {
return resolveRecipient(new RecipientAddress(serviceId.uuid()), false, false);
}
@Override

View file

@ -65,8 +65,8 @@ public class Utils {
if (isUuidCapable) {
// Version 2: UUID user
version = 2;
ownId = ownAddress.getAci().toByteArray();
theirId = theirAddress.getAci().toByteArray();
ownId = ownAddress.getServiceId().toByteArray();
theirId = theirAddress.getServiceId().toByteArray();
} else {
// Version 1: E164 user
version = 1;