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

View file

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

View file

@ -14,7 +14,7 @@ repositories {
} }
dependencies { 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.fasterxml.jackson.core", "jackson-databind", "2.13.1")
implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4")
implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70")

View file

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

View file

@ -79,7 +79,7 @@ class ProvisioningManagerImpl implements ProvisioningManager {
groupsV2Operations = null; groupsV2Operations = null;
} }
accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), 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, userAgent,
groupsV2Operations, groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY); ServiceConfig.AUTOMATIC_NETWORK_RETRY);
@ -97,6 +97,7 @@ class ProvisioningManagerImpl implements ProvisioningManager {
var ret = accountManager.getNewDeviceRegistration(tempIdentityKey); var ret = accountManager.getNewDeviceRegistration(tempIdentityKey);
var number = ret.getNumber(); var number = ret.getNumber();
var aci = ret.getAci(); var aci = ret.getAci();
var pni = ret.getPni();
logger.info("Received link information from {}, linking in progress ...", number); logger.info("Received link information from {}, linking in progress ...", number);
@ -117,7 +118,7 @@ class ProvisioningManagerImpl implements ProvisioningManager {
var encryptedDeviceName = deviceName == null var encryptedDeviceName = deviceName == null
? null ? null
: DeviceNameUtil.encryptDeviceName(deviceName, ret.getIdentity().getPrivateKey()); : DeviceNameUtil.encryptDeviceName(deviceName, ret.getAciIdentity().getPrivateKey());
logger.debug("Finishing new device registration"); logger.debug("Finishing new device registration");
var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(), var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(),
@ -135,10 +136,12 @@ class ProvisioningManagerImpl implements ProvisioningManager {
accountPath, accountPath,
number, number,
aci, aci,
pni,
password, password,
encryptedDeviceName, encryptedDeviceName,
deviceId, deviceId,
ret.getIdentity(), ret.getAciIdentity(),
ret.getPniIdentity(),
registrationId, registrationId,
profileKey, profileKey,
TrustNewIdentity.ON_FIRST_USE); 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.ClientZkOperations;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.push.ACI; 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.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.internal.ServiceResponse; import org.whispersystems.signalservice.internal.ServiceResponse;
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; import org.whispersystems.signalservice.internal.push.VerifyAccountResponse;
@ -79,7 +80,7 @@ class RegistrationManagerImpl implements RegistrationManager {
this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
new DynamicCredentialsProvider( new DynamicCredentialsProvider(
// Using empty UUID, because registering doesn't work otherwise // 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, userAgent,
groupsV2Operations, groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY); ServiceConfig.AUTOMATIC_NETWORK_RETRY);
@ -116,7 +117,8 @@ class RegistrationManagerImpl implements RegistrationManager {
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
final var aci = ACI.parseOrNull(response.getUuid()); 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); accountFileUpdater.updateAccountIdentifiers(account.getNumber(), aci);
ManagerImpl m = null; ManagerImpl m = null;

View file

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

View file

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

View file

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

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.InvalidKeyException;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.ACI; 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.push.exceptions.AuthorizationFailedException;
import org.whispersystems.signalservice.api.util.DeviceNameUtil; import org.whispersystems.signalservice.api.util.DeviceNameUtil;
@ -55,9 +56,12 @@ public class AccountHelper {
} }
try { try {
context.getPreKeyHelper().refreshPreKeysIfNecessary(); context.getPreKeyHelper().refreshPreKeysIfNecessary();
if (account.getAci() == null) { if (account.getAci() == null || account.getPni() == null) {
checkWhoAmiI(); checkWhoAmiI();
} }
if (!account.isMasterDevice() && account.getPniIdentityKeyPair() == null) {
context.getSyncHelper().requestSyncPniIdentity();
}
updateAccountAttributes(); updateAccountAttributes();
} catch (AuthorizationFailedException e) { } catch (AuthorizationFailedException e) {
account.setRegistered(false); account.setRegistered(false);
@ -69,16 +73,18 @@ public class AccountHelper {
final var whoAmI = dependencies.getAccountManager().getWhoAmI(); final var whoAmI = dependencies.getAccountManager().getWhoAmI();
final var number = whoAmI.getNumber(); final var number = whoAmI.getNumber();
final var aci = ACI.parseOrNull(whoAmI.getAci()); 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; 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.setNumber(number);
account.setAci(aci); account.setAci(aci);
account.setPni(pni);
account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress()); account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress());
// TODO check and update remote storage // TODO check and update remote storage
context.getUnidentifiedAccessHelper().rotateSenderCertificates(); context.getUnidentifiedAccessHelper().rotateSenderCertificates();
@ -103,11 +109,11 @@ public class AccountHelper {
(verificationCode1, registrationLock) -> dependencies.getAccountManager() (verificationCode1, registrationLock) -> dependencies.getAccountManager()
.changeNumber(verificationCode1, newNumber, registrationLock)); .changeNumber(verificationCode1, newNumber, registrationLock));
// TODO handle response // TODO handle response
updateSelfIdentifiers(newNumber, account.getAci()); updateSelfIdentifiers(newNumber, account.getAci(), PNI.parseOrThrow(result.first().getPni()));
} }
public void setDeviceName(String deviceName) { public void setDeviceName(String deviceName) {
final var privateKey = account.getIdentityKeyPair().getPrivateKey(); final var privateKey = account.getAciIdentityKeyPair().getPrivateKey();
final var encryptedDeviceName = DeviceNameUtil.encryptDeviceName(deviceName, privateKey); final var encryptedDeviceName = DeviceNameUtil.encryptDeviceName(deviceName, privateKey);
account.setEncryptedDeviceName(encryptedDeviceName); account.setEncryptedDeviceName(encryptedDeviceName);
} }
@ -127,15 +133,15 @@ public class AccountHelper {
} }
public void addDevice(DeviceLinkInfo deviceLinkInfo) throws IOException, InvalidDeviceLinkException { public void addDevice(DeviceLinkInfo deviceLinkInfo) throws IOException, InvalidDeviceLinkException {
var identityKeyPair = account.getIdentityKeyPair();
var verificationCode = dependencies.getAccountManager().getNewDeviceVerificationCode(); var verificationCode = dependencies.getAccountManager().getNewDeviceVerificationCode();
try { try {
dependencies.getAccountManager() dependencies.getAccountManager()
.addDevice(deviceLinkInfo.deviceIdentifier(), .addDevice(deviceLinkInfo.deviceIdentifier(),
deviceLinkInfo.deviceKey(), deviceLinkInfo.deviceKey(),
identityKeyPair, account.getAciIdentityKeyPair(),
Optional.of(account.getProfileKey().serialize()), account.getPniIdentityKeyPair(),
account.getProfileKey(),
verificationCode); verificationCode);
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
throw new InvalidDeviceLinkException("Invalid device link", 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.InvalidGroupStateException;
import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException; import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException;
import org.whispersystems.signalservice.api.push.ACI; 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.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
@ -150,7 +151,7 @@ class GroupV2Helper {
final var memberList = new ArrayList<>(members); final var memberList = new ArrayList<>(members);
final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream(); final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream();
final var uuids = 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, var candidates = Utils.zip(uuids,
credentials, credentials,
(uuid, credential) -> new GroupCandidate(uuid, Optional.fromNullable(credential))) (uuid, credential) -> new GroupCandidate(uuid, Optional.fromNullable(credential)))
@ -218,7 +219,7 @@ class GroupV2Helper {
final var memberList = new ArrayList<>(newMembers); final var memberList = new ArrayList<>(newMembers);
final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream(); final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream();
final var uuids = 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, var candidates = Utils.zip(uuids,
credentials, credentials,
(uuid, credential) -> new GroupCandidate(uuid, Optional.fromNullable(credential))) (uuid, credential) -> new GroupCandidate(uuid, Optional.fromNullable(credential)))
@ -245,8 +246,8 @@ class GroupV2Helper {
final var adminUuids = membersToMakeAdmin.stream() final var adminUuids = membersToMakeAdmin.stream()
.map(context.getRecipientHelper()::resolveSignalServiceAddress) .map(context.getRecipientHelper()::resolveSignalServiceAddress)
.map(SignalServiceAddress::getAci) .map(SignalServiceAddress::getServiceId)
.map(ACI::uuid) .map(ServiceId::uuid)
.toList(); .toList();
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
return commitChange(groupInfoV2, return commitChange(groupInfoV2,
@ -258,8 +259,8 @@ class GroupV2Helper {
) throws IOException { ) throws IOException {
final var memberUuids = members.stream() final var memberUuids = members.stream()
.map(context.getRecipientHelper()::resolveSignalServiceAddress) .map(context.getRecipientHelper()::resolveSignalServiceAddress)
.map(SignalServiceAddress::getAci) .map(SignalServiceAddress::getServiceId)
.map(ACI::uuid) .map(ServiceId::uuid)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
return ejectMembers(groupInfoV2, memberUuids); return ejectMembers(groupInfoV2, memberUuids);
} }
@ -270,8 +271,8 @@ class GroupV2Helper {
var pendingMembersList = groupInfoV2.getGroup().getPendingMembersList(); var pendingMembersList = groupInfoV2.getGroup().getPendingMembersList();
final var memberUuids = members.stream() final var memberUuids = members.stream()
.map(context.getRecipientHelper()::resolveSignalServiceAddress) .map(context.getRecipientHelper()::resolveSignalServiceAddress)
.map(SignalServiceAddress::getAci) .map(SignalServiceAddress::getServiceId)
.map(ACI::uuid) .map(ServiceId::uuid)
.map(uuid -> DecryptedGroupUtil.findPendingByUuid(pendingMembersList, uuid)) .map(uuid -> DecryptedGroupUtil.findPendingByUuid(pendingMembersList, uuid))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
@ -343,7 +344,7 @@ class GroupV2Helper {
change.setSourceUuid(context.getRecipientHelper() change.setSourceUuid(context.getRecipientHelper()
.resolveSignalServiceAddress(selfRecipientId) .resolveSignalServiceAddress(selfRecipientId)
.getAci() .getServiceId()
.toByteString()); .toByteString());
return commitChange(groupSecretParams, decryptedGroupJoinInfo.getRevision(), change, groupLinkPassword); return commitChange(groupSecretParams, decryptedGroupJoinInfo.getRevision(), change, groupLinkPassword);
@ -360,7 +361,7 @@ class GroupV2Helper {
final var change = groupOperations.createAcceptInviteChange(profileKeyCredential); 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()); change.setSourceUuid(aci.toByteString());
return commitChange(groupInfoV2, change); return commitChange(groupInfoV2, change);
@ -372,7 +373,7 @@ class GroupV2Helper {
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
final var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId); final var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId);
final var newRole = admin ? Member.Role.ADMINISTRATOR : Member.Role.DEFAULT; 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); return commitChange(groupInfoV2, change);
} }

View file

@ -77,7 +77,7 @@ public class IdentityHelper {
) { ) {
return Utils.computeSafetyNumber(capabilities.isUuid(), return Utils.computeSafetyNumber(capabilities.isUuid(),
account.getSelfAddress(), account.getSelfAddress(),
account.getIdentityKeyPair().getPublicKey(), account.getAciIdentityKeyPair().getPublicKey(),
theirAddress, theirAddress,
theirIdentityKey); 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.RetrieveStorageDataAction;
import org.asamk.signal.manager.actions.SendGroupInfoAction; import org.asamk.signal.manager.actions.SendGroupInfoAction;
import org.asamk.signal.manager.actions.SendGroupInfoRequestAction; 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.SendReceiptAction;
import org.asamk.signal.manager.actions.SendRetryMessageRequestAction; import org.asamk.signal.manager.actions.SendRetryMessageRequestAction;
import org.asamk.signal.manager.actions.SendSyncBlockedListAction; 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.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.storage.stickers.Sticker; 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.ProtocolInvalidKeyException;
import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException; import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException;
import org.signal.libsignal.metadata.ProtocolInvalidMessageException; import org.signal.libsignal.metadata.ProtocolInvalidMessageException;
@ -340,6 +342,9 @@ public final class IncomingMessageHandler {
if (rm.isConfigurationRequest()) { if (rm.isConfigurationRequest()) {
actions.add(SendSyncConfigurationAction.create()); actions.add(SendSyncConfigurationAction.create());
} }
if (rm.isPniIdentityRequest()) {
actions.add(SendPniIdentityKeyAction.create());
}
} }
if (syncMessage.getGroups().isPresent()) { if (syncMessage.getGroups().isPresent()) {
logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring."); logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring.");
@ -440,6 +445,11 @@ public final class IncomingMessageHandler {
.get()); .get());
} }
} }
if (syncMessage.getPniIdentity().isPresent()) {
final var pniIdentity = syncMessage.getPniIdentity().get();
account.setPniIdentityKeyPair(KeyUtils.getIdentityKeyPair(pniIdentity.getPublicKey().toByteArray(),
pniIdentity.getPrivateKey().toByteArray()));
}
return actions; return actions;
} }

View file

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

View file

@ -1,5 +1,7 @@
package org.asamk.signal.manager.helper; package org.asamk.signal.manager.helper;
import com.google.protobuf.ByteString;
import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.api.TrustLevel;
import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.groups.GroupInfoV1;
@ -49,11 +51,20 @@ public class SyncHelper {
} }
public void requestAllSyncData() { public void requestAllSyncData() {
requestSyncGroups(); requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.GROUPS);
requestSyncContacts(); requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS);
requestSyncBlocked(); requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED);
requestSyncConfiguration(); requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION);
requestSyncKeys(); requestSyncKeys();
requestSyncPniIdentity();
}
public void requestSyncKeys() {
requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.KEYS);
}
public void requestSyncPniIdentity() {
requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.PNI_IDENTITY);
} }
public void sendSyncFetchProfileMessage() { public void sendSyncFetchProfileMessage() {
@ -217,6 +228,15 @@ public class SyncHelper {
context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forConfiguration(configurationMessage)); 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 { public void handleSyncDeviceContacts(final InputStream input) throws IOException {
final var s = new DeviceContactsInputStream(input); final var s = new DeviceContactsInputStream(input);
DeviceContact c; DeviceContact c;
@ -270,42 +290,8 @@ public class SyncHelper {
} }
} }
private void requestSyncGroups() { private void requestSyncData(final SignalServiceProtos.SyncMessage.Request.Type type) {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder() var r = SignalServiceProtos.SyncMessage.Request.newBuilder().setType(type).build();
.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();
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
context.getSendHelper().sendSyncMessage(message); 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.crypto.UnidentifiedAccess;
import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.kbs.MasterKey;
import org.whispersystems.signalservice.api.push.ACI; 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.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.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.storage.StorageKey; import org.whispersystems.signalservice.api.storage.StorageKey;
import org.whispersystems.signalservice.api.util.CredentialsProvider; import org.whispersystems.signalservice.api.util.CredentialsProvider;
@ -94,6 +95,7 @@ public class SignalAccount implements Closeable {
private String accountPath; private String accountPath;
private String number; private String number;
private ACI aci; private ACI aci;
private PNI pni;
private String encryptedDeviceName; private String encryptedDeviceName;
private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
private boolean isMultiDevice = false; private boolean isMultiDevice = false;
@ -105,7 +107,8 @@ public class SignalAccount implements Closeable {
private ProfileKey profileKey; private ProfileKey profileKey;
private int preKeyIdOffset = 1; private int preKeyIdOffset = 1;
private int nextSignedPreKeyId = 1; private int nextSignedPreKeyId = 1;
private IdentityKeyPair identityKeyPair; private IdentityKeyPair aciIdentityKeyPair;
private IdentityKeyPair pniIdentityKeyPair;
private int localRegistrationId; private int localRegistrationId;
private TrustNewIdentity trustNewIdentity; private TrustNewIdentity trustNewIdentity;
private long lastReceiveTimestamp = 0; private long lastReceiveTimestamp = 0;
@ -161,7 +164,8 @@ public class SignalAccount implements Closeable {
File dataPath, File dataPath,
String accountPath, String accountPath,
String number, String number,
IdentityKeyPair identityKey, IdentityKeyPair aciIdentityKey,
IdentityKeyPair pniIdentityKey,
int registrationId, int registrationId,
ProfileKey profileKey, ProfileKey profileKey,
final TrustNewIdentity trustNewIdentity final TrustNewIdentity trustNewIdentity
@ -180,7 +184,8 @@ public class SignalAccount implements Closeable {
signalAccount.profileKey = profileKey; signalAccount.profileKey = profileKey;
signalAccount.dataPath = dataPath; signalAccount.dataPath = dataPath;
signalAccount.identityKeyPair = identityKey; signalAccount.aciIdentityKeyPair = aciIdentityKey;
signalAccount.pniIdentityKeyPair = pniIdentityKey;
signalAccount.localRegistrationId = registrationId; signalAccount.localRegistrationId = registrationId;
signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.trustNewIdentity = trustNewIdentity;
signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath),
@ -203,10 +208,12 @@ public class SignalAccount implements Closeable {
String accountPath, String accountPath,
String number, String number,
ACI aci, ACI aci,
PNI pni,
String password, String password,
String encryptedDeviceName, String encryptedDeviceName,
int deviceId, int deviceId,
IdentityKeyPair identityKey, IdentityKeyPair aciIdentityKey,
IdentityKeyPair pniIdentityKey,
int registrationId, int registrationId,
ProfileKey profileKey, ProfileKey profileKey,
final TrustNewIdentity trustNewIdentity final TrustNewIdentity trustNewIdentity
@ -218,17 +225,27 @@ public class SignalAccount implements Closeable {
accountPath, accountPath,
number, number,
aci, aci,
pni,
password, password,
encryptedDeviceName, encryptedDeviceName,
deviceId, deviceId,
identityKey, aciIdentityKey,
pniIdentityKey,
registrationId, registrationId,
profileKey, profileKey,
trustNewIdentity); trustNewIdentity);
} }
final var signalAccount = load(dataPath, accountPath, true, 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.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress());
signalAccount.getSessionStore().archiveAllSessions(); signalAccount.getSessionStore().archiveAllSessions();
signalAccount.getSenderKeyStore().deleteAll(); signalAccount.getSenderKeyStore().deleteAll();
@ -253,10 +270,12 @@ public class SignalAccount implements Closeable {
String accountPath, String accountPath,
String number, String number,
ACI aci, ACI aci,
PNI pni,
String password, String password,
String encryptedDeviceName, String encryptedDeviceName,
int deviceId, int deviceId,
IdentityKeyPair identityKey, IdentityKeyPair aciIdentityKey,
IdentityKeyPair pniIdentityKey,
int registrationId, int registrationId,
ProfileKey profileKey, ProfileKey profileKey,
final TrustNewIdentity trustNewIdentity final TrustNewIdentity trustNewIdentity
@ -267,11 +286,18 @@ public class SignalAccount implements Closeable {
final var pair = openFileChannel(fileName, true); final var pair = openFileChannel(fileName, true);
var signalAccount = new SignalAccount(pair.first(), pair.second()); 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.dataPath = dataPath;
signalAccount.accountPath = accountPath; signalAccount.accountPath = accountPath;
signalAccount.identityKeyPair = identityKey;
signalAccount.localRegistrationId = registrationId; signalAccount.localRegistrationId = registrationId;
signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.trustNewIdentity = trustNewIdentity;
signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath),
@ -283,6 +309,7 @@ public class SignalAccount implements Closeable {
signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress());
signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION; signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION;
signalAccount.migrateLegacyConfigs(); signalAccount.migrateLegacyConfigs();
signalAccount.clearAllPreKeys();
signalAccount.save(); signalAccount.save();
return signalAccount; return signalAccount;
@ -291,17 +318,23 @@ public class SignalAccount implements Closeable {
private void setProvisioningData( private void setProvisioningData(
final String number, final String number,
final ACI aci, final ACI aci,
final PNI pni,
final String password, final String password,
final String encryptedDeviceName, final String encryptedDeviceName,
final int deviceId, final int deviceId,
final IdentityKeyPair aciIdentity,
final IdentityKeyPair pniIdentity,
final ProfileKey profileKey final ProfileKey profileKey
) { ) {
this.number = number; this.number = number;
this.aci = aci; this.aci = aci;
this.pni = pni;
this.password = password; this.password = password;
this.profileKey = profileKey; this.profileKey = profileKey;
this.encryptedDeviceName = encryptedDeviceName; this.encryptedDeviceName = encryptedDeviceName;
this.deviceId = deviceId; this.deviceId = deviceId;
this.aciIdentityKeyPair = aciIdentity;
this.pniIdentityKeyPair = pniIdentity;
this.registered = true; this.registered = true;
this.isMultiDevice = true; this.isMultiDevice = true;
this.lastReceiveTimestamp = 0; this.lastReceiveTimestamp = 0;
@ -330,6 +363,9 @@ public class SignalAccount implements Closeable {
} }
save(); save();
} }
if (isMasterDevice() && getPniIdentityKeyPair() == null) {
setPniIdentityKeyPair(KeyUtils.generateIdentityKeyPair());
}
} }
private void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) { private void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) {
@ -440,7 +476,14 @@ public class SignalAccount implements Closeable {
try { try {
aci = ACI.parseOrThrow(rootNode.get("uuid").asText()); aci = ACI.parseOrThrow(rootNode.get("uuid").asText());
} catch (IllegalArgumentException e) { } 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")) { if (rootNode.hasNonNull("deviceName")) {
@ -459,11 +502,16 @@ public class SignalAccount implements Closeable {
if (rootNode.hasNonNull("registrationId")) { if (rootNode.hasNonNull("registrationId")) {
registrationId = rootNode.get("registrationId").asInt(); registrationId = rootNode.get("registrationId").asInt();
} }
IdentityKeyPair identityKeyPair = null; IdentityKeyPair aciIdentityKeyPair = null;
if (rootNode.hasNonNull("identityPrivateKey") && rootNode.hasNonNull("identityKey")) { if (rootNode.hasNonNull("identityPrivateKey") && rootNode.hasNonNull("identityKey")) {
final var publicKeyBytes = Base64.getDecoder().decode(rootNode.get("identityKey").asText()); final var publicKeyBytes = Base64.getDecoder().decode(rootNode.get("identityKey").asText());
final var privateKeyBytes = Base64.getDecoder().decode(rootNode.get("identityPrivateKey").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")) { if (rootNode.hasNonNull("registrationLockPin")) {
@ -504,12 +552,12 @@ public class SignalAccount implements Closeable {
LegacyJsonSignalProtocolStore.class) LegacyJsonSignalProtocolStore.class)
: null; : null;
if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) { if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) {
identityKeyPair = legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentityKeyPair(); aciIdentityKeyPair = legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentityKeyPair();
registrationId = legacySignalProtocolStore.getLegacyIdentityKeyStore().getLocalRegistrationId(); registrationId = legacySignalProtocolStore.getLegacyIdentityKeyStore().getLocalRegistrationId();
migratedLegacyConfig = true; migratedLegacyConfig = true;
} }
this.identityKeyPair = identityKeyPair; this.aciIdentityKeyPair = aciIdentityKeyPair;
this.localRegistrationId = registrationId; this.localRegistrationId = registrationId;
this.trustNewIdentity = trustNewIdentity; this.trustNewIdentity = trustNewIdentity;
@ -741,6 +789,7 @@ public class SignalAccount implements Closeable {
rootNode.put("version", CURRENT_STORAGE_VERSION) rootNode.put("version", CURRENT_STORAGE_VERSION)
.put("username", number) .put("username", number)
.put("uuid", aci == null ? null : aci.toString()) .put("uuid", aci == null ? null : aci.toString())
.put("pni", pni == null ? null : pni.toString())
.put("deviceName", encryptedDeviceName) .put("deviceName", encryptedDeviceName)
.put("deviceId", deviceId) .put("deviceId", deviceId)
.put("isMultiDevice", isMultiDevice) .put("isMultiDevice", isMultiDevice)
@ -748,8 +797,18 @@ public class SignalAccount implements Closeable {
.put("password", password) .put("password", password)
.put("registrationId", localRegistrationId) .put("registrationId", localRegistrationId)
.put("identityPrivateKey", .put("identityPrivateKey",
Base64.getEncoder().encodeToString(identityKeyPair.getPrivateKey().serialize())) Base64.getEncoder().encodeToString(aciIdentityKeyPair.getPrivateKey().serialize()))
.put("identityKey", Base64.getEncoder().encodeToString(identityKeyPair.getPublicKey().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("registrationLockPin", registrationLockPin)
.put("pinMasterKey", .put("pinMasterKey",
pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize())) pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize()))
@ -820,8 +879,14 @@ public class SignalAccount implements Closeable {
public SignalServiceDataStore getSignalServiceDataStore() { public SignalServiceDataStore getSignalServiceDataStore() {
return new SignalServiceDataStore() { return new SignalServiceDataStore() {
@Override @Override
public SignalServiceAccountDataStore get(final AccountIdentifier accountIdentifier) { public SignalServiceAccountDataStore get(final ServiceId accountIdentifier) {
if (accountIdentifier.equals(aci)) {
return getSignalServiceAccountDataStore(); 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 @Override
@ -831,7 +896,7 @@ public class SignalAccount implements Closeable {
@Override @Override
public SignalServiceAccountDataStore pni() { public SignalServiceAccountDataStore pni() {
return getSignalServiceAccountDataStore(); throw new AssertionError("PNI not to be used yet!");
} }
@Override @Override
@ -870,7 +935,7 @@ public class SignalAccount implements Closeable {
return getOrCreate(() -> identityKeyStore, return getOrCreate(() -> identityKeyStore,
() -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, accountPath), () -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, accountPath),
getRecipientStore(), getRecipientStore(),
identityKeyPair, aciIdentityKeyPair,
localRegistrationId, localRegistrationId,
trustNewIdentity)); trustNewIdentity));
} }
@ -937,6 +1002,11 @@ public class SignalAccount implements Closeable {
return aci; return aci;
} }
@Override
public PNI getPni() {
return pni;
}
@Override @Override
public String getE164() { public String getE164() {
return number; return number;
@ -972,6 +1042,15 @@ public class SignalAccount implements Closeable {
save(); save();
} }
public PNI getPni() {
return pni;
}
public void setPni(final PNI pni) {
this.pni = pni;
save();
}
public SignalServiceAddress getSelfAddress() { public SignalServiceAddress getSelfAddress() {
return new SignalServiceAddress(aci, number); return new SignalServiceAddress(aci, number);
} }
@ -1001,8 +1080,17 @@ public class SignalAccount implements Closeable {
return deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID; return deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID;
} }
public IdentityKeyPair getIdentityKeyPair() { public IdentityKeyPair getAciIdentityKeyPair() {
return identityKeyPair; return aciIdentityKeyPair;
}
public IdentityKeyPair getPniIdentityKeyPair() {
return pniIdentityKeyPair;
}
public void setPniIdentityKeyPair(final IdentityKeyPair identityKeyPair) {
pniIdentityKeyPair = identityKeyPair;
save();
} }
public int getLocalRegistrationId() { public int getLocalRegistrationId() {
@ -1118,7 +1206,7 @@ public class SignalAccount implements Closeable {
return configurationStore.getPhoneNumberUnlisted() == null || !configurationStore.getPhoneNumberUnlisted(); 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.pinMasterKey = masterKey;
this.storageManifestVersion = -1; this.storageManifestVersion = -1;
this.storageKey = null; this.storageKey = null;
@ -1127,6 +1215,7 @@ public class SignalAccount implements Closeable {
this.isMultiDevice = false; this.isMultiDevice = false;
this.registered = true; this.registered = true;
this.aci = aci; this.aci = aci;
this.pni = pni;
this.registrationLockPin = pin; this.registrationLockPin = pin;
this.lastReceiveTimestamp = 0; this.lastReceiveTimestamp = 0;
save(); save();
@ -1135,7 +1224,7 @@ public class SignalAccount implements Closeable {
getSessionStore().archiveAllSessions(); getSessionStore().archiveAllSessions();
getSenderKeyStore().deleteAll(); getSenderKeyStore().deleteAll();
final var recipientId = getRecipientStore().resolveSelfRecipientTrusted(getSelfRecipientAddress()); final var recipientId = getRecipientStore().resolveSelfRecipientTrusted(getSelfRecipientAddress());
final var publicKey = getIdentityKeyPair().getPublicKey(); final var publicKey = getAciIdentityKeyPair().getPublicKey();
getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date()); getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date());
getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED); 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) { 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) { public RecipientAddress(UUID uuid) {

View file

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

View file

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

View file

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