From: AsamK Date: Sun, 16 Oct 2022 18:07:33 +0000 (+0200) Subject: Handle PniChangeNumber X-Git-Tag: v0.11.4~14 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/45a5795c9c0982302ab75aec74707ff37e1ee67f Handle PniChangeNumber --- diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index ae431ffb..1e24d6c7 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } 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") diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java index 0b19a691..7ae9aa45 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java @@ -11,12 +11,15 @@ import org.asamk.signal.manager.config.ServiceConfig; 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; @@ -111,6 +114,19 @@ public class AccountHelper { 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 { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java index 09e3ff95..84747e9c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java @@ -77,6 +77,10 @@ class GroupV2Helper { this.context = context; } + void clearAuthCredentialCache() { + groupApiCredentials = null; + } + DecryptedGroup getDecryptedGroup(final GroupSecretParams groupSecretParams) throws NotAGroupMemberException { try { final var groupsV2AuthorizationString = getGroupAuthForToday(groupSecretParams); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java index a9c8fdae..69ad65f7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java @@ -42,8 +42,10 @@ import org.signal.libsignal.metadata.ProtocolInvalidMessageException; 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; @@ -58,6 +60,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage 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; @@ -304,7 +307,10 @@ public final class IncomingMessageHandler { 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; @@ -364,7 +370,10 @@ public final class IncomingMessageHandler { } private List 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(); account.setMultiDevice(true); @@ -519,7 +528,26 @@ public final class IncomingMessageHandler { 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; } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java index e8b19356..6d2021e0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java @@ -352,7 +352,8 @@ public class SendHelper { contentHint, message, SignalServiceMessageSender.SenderKeyGroupEvents.EMPTY, - urgent); + urgent, + false); synchronized (entryId) { if (entryId.get() == -1) { final var newId = messageSendLogStore.insertIfPossible(message.getTimestamp(), diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 85e5cadd..730fe85a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -1297,11 +1297,31 @@ public class SignalAccount implements Closeable { 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); } @@ -1359,6 +1379,11 @@ public class SignalAccount implements Closeable { return localPniRegistrationId; } + public void setLocalPniRegistrationId(final int localPniRegistrationId) { + this.localPniRegistrationId = localPniRegistrationId; + save(); + } + public String getPassword() { return password; } diff --git a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java index 7e8ebea5..f61ed2a2 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java @@ -19,7 +19,7 @@ public class MessageCacheUtils { 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; } @@ -71,6 +71,10 @@ public class MessageCacheUtils { if (version >= 7) { isStory = in.readBoolean(); } + String updatedPni = null; + if (version >= 8) { + updatedPni = in.readUTF(); + } Optional addressOptional = sourceServiceId == null ? Optional.empty() : Optional.of(new SignalServiceAddress(sourceServiceId, source)); @@ -84,6 +88,7 @@ public class MessageCacheUtils { uuid, destinationUuid == null ? UuidUtil.UNKNOWN_UUID.toString() : destinationUuid, isUrgent, + updatedPni == null ? "" : updatedPni, isStory); } } @@ -91,7 +96,7 @@ public class MessageCacheUtils { 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() : ""); @@ -111,6 +116,7 @@ public class MessageCacheUtils { out.writeLong(envelope.getServerDeliveredTimestamp()); out.writeBoolean(envelope.isUrgent()); out.writeBoolean(envelope.isStory()); + out.writeUTF(envelope.getUpdatedPni() == null ? "" : envelope.getUpdatedPni()); } } }