}
dependencies {
- implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_61")
+ implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_62")
implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.4")
implementation("com.google.protobuf", "protobuf-javalite", "3.21.6")
implementation("org.bouncycastle", "bcprov-jdk15on", "1.70")
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.util.KeyUtils;
import org.asamk.signal.manager.util.NumberVerificationUtils;
+import org.signal.libsignal.protocol.IdentityKeyPair;
import org.signal.libsignal.protocol.InvalidKeyException;
+import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.PNI;
+import org.whispersystems.signalservice.api.push.ServiceIdType;
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity;
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
import org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException;
context.getAccountFileUpdater().updateAccountIdentifiers(account.getNumber(), account.getAci());
}
+ public void setPni(
+ final PNI updatedPni,
+ final IdentityKeyPair pniIdentityKeyPair,
+ final SignedPreKeyRecord pniSignedPreKey,
+ final int localPniRegistrationId
+ ) throws IOException {
+ account.setPni(updatedPni, pniIdentityKeyPair, pniSignedPreKey, localPniRegistrationId);
+ context.getPreKeyHelper().refreshPreKeysIfNecessary(ServiceIdType.PNI);
+ if (account.getPni() == null || !account.getPni().equals(updatedPni)) {
+ context.getGroupV2Helper().clearAuthCredentialCache();
+ }
+ }
+
public void startChangeNumber(
String newNumber, String captcha, boolean voiceVerification
) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException {
this.context = context;
}
+ void clearAuthCredentialCache() {
+ groupApiCredentials = null;
+ }
+
DecryptedGroup getDecryptedGroup(final GroupSecretParams groupSecretParams) throws NotAGroupMemberException {
try {
final var groupsV2AuthorizationString = getGroupAuthForToday(groupSecretParams);
import org.signal.libsignal.metadata.ProtocolNoSessionException;
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
import org.signal.libsignal.metadata.SelfSendException;
+import org.signal.libsignal.protocol.IdentityKeyPair;
import org.signal.libsignal.protocol.InvalidMessageException;
import org.signal.libsignal.protocol.message.DecryptionErrorMessage;
+import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.slf4j.Logger;
import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
+import org.whispersystems.signalservice.api.push.PNI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
if (content.getSyncMessage().isPresent()) {
var syncMessage = content.getSyncMessage().get();
- actions.addAll(handleSyncMessage(syncMessage, senderDeviceAddress, receiveConfig.ignoreAttachments()));
+ actions.addAll(handleSyncMessage(envelope,
+ syncMessage,
+ senderDeviceAddress,
+ receiveConfig.ignoreAttachments()));
}
return actions;
}
private List<HandleAction> handleSyncMessage(
- final SignalServiceSyncMessage syncMessage, final DeviceAddress sender, final boolean ignoreAttachments
+ final SignalServiceEnvelope envelope,
+ final SignalServiceSyncMessage syncMessage,
+ final DeviceAddress sender,
+ final boolean ignoreAttachments
) {
var actions = new ArrayList<HandleAction>();
account.setMultiDevice(true);
pniIdentity.getPrivateKey().toByteArray()));
actions.add(RefreshPreKeysAction.create());
}
- // TODO handle PniChangeNumber
+ if (syncMessage.getPniChangeNumber().isPresent()) {
+ final var pniChangeNumber = syncMessage.getPniChangeNumber().get();
+ logger.debug("Received PNI change number sync message, applying.");
+ if (pniChangeNumber.hasIdentityKeyPair()
+ && pniChangeNumber.hasRegistrationId()
+ && pniChangeNumber.hasSignedPreKey()
+ && !envelope.getUpdatedPni().isEmpty()) {
+ logger.debug("New PNI: {}", envelope.getUpdatedPni());
+ try {
+ final var updatedPni = PNI.parseOrThrow(envelope.getUpdatedPni());
+ context.getAccountHelper()
+ .setPni(updatedPni,
+ new IdentityKeyPair(pniChangeNumber.getIdentityKeyPair().toByteArray()),
+ new SignedPreKeyRecord(pniChangeNumber.getSignedPreKey().toByteArray()),
+ pniChangeNumber.getRegistrationId());
+ } catch (Exception e) {
+ logger.warn("Failed to handle change number message", e);
+ }
+ }
+ }
return actions;
}
contentHint,
message,
SignalServiceMessageSender.SenderKeyGroupEvents.EMPTY,
- urgent);
+ urgent,
+ false);
synchronized (entryId) {
if (entryId.get() == -1) {
final var newId = messageSendLogStore.insertIfPossible(message.getTimestamp(),
return pni;
}
- public void setPni(final PNI pni) {
- this.pni = pni;
+ public void setPni(final PNI updatedPni) {
+ if (this.pni != null && !this.pni.equals(updatedPni)) {
+ // Clear data for old PNI
+ identityKeyStore.deleteIdentity(this.pni);
+ getPniPreKeyStore().removeAllPreKeys();
+ getPniSignedPreKeyStore().removeAllSignedPreKeys();
+ }
+
+ this.pni = updatedPni;
save();
}
+ public void setPni(
+ final PNI updatedPni,
+ final IdentityKeyPair pniIdentityKeyPair,
+ final SignedPreKeyRecord pniSignedPreKey,
+ final int localPniRegistrationId
+ ) {
+ setPni(updatedPni);
+
+ setPniIdentityKeyPair(pniIdentityKeyPair);
+ addPniSignedPreKey(pniSignedPreKey);
+ setLocalPniRegistrationId(localPniRegistrationId);
+ }
+
public SignalServiceAddress getSelfAddress() {
return new SignalServiceAddress(aci, number);
}
return localPniRegistrationId;
}
+ public void setLocalPniRegistrationId(final int localPniRegistrationId) {
+ this.localPniRegistrationId = localPniRegistrationId;
+ save();
+ }
+
public String getPassword() {
return password;
}
try (var f = new FileInputStream(file)) {
var in = new DataInputStream(f);
var version = in.readInt();
- if (version > 7) {
+ if (version > 8) {
// Unsupported envelope version
return null;
}
if (version >= 7) {
isStory = in.readBoolean();
}
+ String updatedPni = null;
+ if (version >= 8) {
+ updatedPni = in.readUTF();
+ }
Optional<SignalServiceAddress> addressOptional = sourceServiceId == null
? Optional.empty()
: Optional.of(new SignalServiceAddress(sourceServiceId, source));
uuid,
destinationUuid == null ? UuidUtil.UNKNOWN_UUID.toString() : destinationUuid,
isUrgent,
+ updatedPni == null ? "" : updatedPni,
isStory);
}
}
public static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException {
try (var f = new FileOutputStream(file)) {
try (var out = new DataOutputStream(f)) {
- out.writeInt(7); // version
+ out.writeInt(8); // version
out.writeInt(envelope.getType());
out.writeUTF(""); // legacy number
out.writeUTF(envelope.getSourceUuid().isPresent() ? envelope.getSourceUuid().get() : "");
out.writeLong(envelope.getServerDeliveredTimestamp());
out.writeBoolean(envelope.isUrgent());
out.writeBoolean(envelope.isStory());
+ out.writeUTF(envelope.getUpdatedPni() == null ? "" : envelope.getUpdatedPni());
}
}
}