"name":"java.lang.Boolean",
"methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]
},
+{
+ "name":"java.lang.Class",
+ "methods":[{"name":"getCanonicalName","parameterTypes":[] }]
+},
{
"name":"java.lang.ClassLoader",
"methods":[
{
"name":"java.lang.NoSuchMethodError"
},
+{
+ "name":"java.lang.Throwable",
+ "methods":[{"name":"getMessage","parameterTypes":[] }]
+},
{
"name":"java.lang.UnsatisfiedLinkError",
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
"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"] }]
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
-{
- "name":"org.whispersystems.signalservice.api.push.AccountIdentifier",
- "allDeclaredFields":true,
- "queryAllDeclaredMethods":true
-},
{
"name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity",
"allDeclaredFields":true,
{
"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_"}
]
},
{
"fields":[
{"name":"bitField0_"},
{"name":"content_"},
+ {"name":"destinationUuid_"},
{"name":"legacyMessage_"},
{"name":"relay_"},
{"name":"serverGuid_"},
{"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":[
}
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")
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) {
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;
}
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);
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);
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(),
accountPath,
number,
aci,
+ pni,
password,
encryptedDeviceName,
deviceId,
- ret.getIdentity(),
+ ret.getAciIdentity(),
+ ret.getPniIdentity(),
registrationId,
profileKey,
TrustNewIdentity.ON_FIRST_USE);
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;
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);
//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;
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);
public SignalServiceAccountManager createUnauthenticatedAccountManager(String number, String password) {
return new SignalServiceAccountManager(getServiceEnvironmentConfig().getSignalServiceConfiguration(),
+ null,
null,
number,
SignalServiceAddress.DEFAULT_DEVICE_ID,
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();
}
}
}
--- /dev/null
+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();
+ }
+}
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;
}
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);
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();
(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);
}
}
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);
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;
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)))
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)))
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,
) 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);
}
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)
change.setSourceUuid(context.getRecipientHelper()
.resolveSignalServiceAddress(selfRecipientId)
- .getAci()
+ .getServiceId()
.toByteString());
return commitChange(groupSecretParams, decryptedGroupJoinInfo.getRevision(), change, groupLinkPassword);
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);
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);
}
) {
return Utils.computeSafetyNumber(capabilities.isUuid(),
account.getSelfAddress(),
- account.getIdentityKeyPair().getPublicKey(),
+ account.getAciIdentityKeyPair().getPublicKey(),
theirAddress,
theirIdentityKey);
}
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;
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;
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.");
.get());
}
}
+ if (syncMessage.getPniIdentity().isPresent()) {
+ final var pniIdentity = syncMessage.getPniIdentity().get();
+ account.setPniIdentityKeyPair(KeyUtils.getIdentityKeyPair(pniIdentity.getPublicKey().toByteArray(),
+ pniIdentity.getPrivateKey().toByteArray()));
+ }
return actions;
}
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;
}
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() {
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;
}
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() {
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;
}
}
- 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);
}
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;
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;
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;
File dataPath,
String accountPath,
String number,
- IdentityKeyPair identityKey,
+ IdentityKeyPair aciIdentityKey,
+ IdentityKeyPair pniIdentityKey,
int registrationId,
ProfileKey profileKey,
final TrustNewIdentity trustNewIdentity
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),
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
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();
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
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),
signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress());
signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION;
signalAccount.migrateLegacyConfigs();
+ signalAccount.clearAllPreKeys();
signalAccount.save();
return signalAccount;
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;
}
save();
}
+ if (isMasterDevice() && getPniIdentityKeyPair() == null) {
+ setPniIdentityKeyPair(KeyUtils.generateIdentityKeyPair());
+ }
}
private void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) {
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")) {
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")) {
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;
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)
.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()))
public SignalServiceDataStore getSignalServiceDataStore() {
return new SignalServiceDataStore() {
@Override
- public SignalServiceAccountDataStore get(final AccountIdentifier accountIdentifier) {
- return getSignalServiceAccountDataStore();
+ 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
@Override
public SignalServiceAccountDataStore pni() {
- return getSignalServiceAccountDataStore();
+ throw new AssertionError("PNI not to be used yet!");
}
@Override
return getOrCreate(() -> identityKeyStore,
() -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, accountPath),
getRecipientStore(),
- identityKeyPair,
+ aciIdentityKeyPair,
localRegistrationId,
trustNewIdentity));
}
return aci;
}
+ @Override
+ public PNI getPni() {
+ return pni;
+ }
+
@Override
public String getE164() {
return number;
save();
}
+ public PNI getPni() {
+ return pni;
+ }
+
+ public void setPni(final PNI pni) {
+ this.pni = pni;
+ save();
+ }
+
public SignalServiceAddress getSelfAddress() {
return new SignalServiceAddress(aci, number);
}
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() {
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;
this.isMultiDevice = false;
this.registered = true;
this.aci = aci;
+ this.pni = pni;
this.registrationLockPin = pin;
this.lastReceiveTimestamp = 0;
save();
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);
}
}
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) {
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 {
RecipientId resolveRecipient(SignalServiceAddress address);
- RecipientId resolveRecipient(ACI aci);
+ RecipientId resolveRecipient(ServiceId aci);
RecipientId resolveRecipient(long recipientId);
}
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;
}
@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
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;