X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/5b8c0c4e2de174de892af65b75a7e4818cbc29c7..8e8eed7b061f1ed47cf9e30abb5e29ee08e3a3dc:/lib/src/main/java/org/asamk/signal/manager/Manager.java diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 3c2d130d..096f1c07 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -16,6 +16,7 @@ */ package org.asamk.signal.manager; +import org.asamk.signal.manager.api.Device; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironment; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; @@ -82,6 +83,7 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceMessagePipe; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageSender; +import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; @@ -108,7 +110,6 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsO import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream; -import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo; import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; @@ -118,6 +119,8 @@ import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException; +import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; +import org.whispersystems.signalservice.api.util.DeviceNameUtil; import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.api.util.SleepTimer; @@ -159,6 +162,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; import java.util.stream.Collectors; @@ -191,6 +195,15 @@ public class Manager implements Closeable { private final PinHelper pinHelper; private final AvatarStore avatarStore; private final AttachmentStore attachmentStore; + private final SignalSessionLock sessionLock = new SignalSessionLock() { + private final ReentrantLock LEGACY_LOCK = new ReentrantLock(); + + @Override + public Lock acquire() { + LEGACY_LOCK.lock(); + return LEGACY_LOCK::unlock; + } + }; Manager( SignalAccount account, @@ -286,7 +299,7 @@ public class Manager implements Closeable { throw new NotRegisteredException(); } - var account = SignalAccount.load(pathConfig.getDataPath(), username); + var account = SignalAccount.load(pathConfig.getDataPath(), username, true); if (!account.isRegistered()) { throw new NotRegisteredException(); @@ -316,11 +329,9 @@ public class Manager implements Closeable { public void checkAccountState() throws IOException { if (accountManager.getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) { refreshPreKeys(); - account.save(); } if (account.getUuid() == null) { account.setUuid(accountManager.getOwnUuid()); - account.save(); } updateAccountAttributes(); } @@ -342,7 +353,8 @@ public class Manager implements Closeable { } public void updateAccountAttributes() throws IOException { - accountManager.setAccountAttributes(null, + accountManager.setAccountAttributes(account.getEncryptedDeviceName(), + null, account.getLocalRegistrationId(), true, // set legacy pin only if no KBS master key is set @@ -355,18 +367,22 @@ public class Manager implements Closeable { } /** - * @param name if null, the previous name will be kept + * @param givenName if null, the previous givenName will be kept + * @param familyName if null, the previous familyName will be kept * @param about if null, the previous about text will be kept * @param aboutEmoji if null, the previous about emoji will be kept * @param avatar if avatar is null the image from the local avatar store is used (if present), - * if it's Optional.absent(), the avatar will be removed */ - public void setProfile(String name, String about, String aboutEmoji, Optional avatar) throws IOException { + public void setProfile( + String givenName, final String familyName, String about, String aboutEmoji, Optional avatar + ) throws IOException { var profile = getRecipientProfile(account.getSelfRecipientId()); var builder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile); - if (name != null) { - builder.withGivenName(name); - builder.withFamilyName(null); + if (givenName != null) { + builder.withGivenName(givenName); + } + if (familyName != null) { + builder.withFamilyName(familyName); } if (about != null) { builder.withAbout(about); @@ -382,8 +398,9 @@ public class Manager implements Closeable { accountManager.setVersionedProfile(account.getUuid(), account.getProfileKey(), newProfile.getInternalServiceName(), - newProfile.getAbout(), - newProfile.getAboutEmoji(), + newProfile.getAbout() == null ? "" : newProfile.getAbout(), + newProfile.getAboutEmoji() == null ? "" : newProfile.getAboutEmoji(), + Optional.absent(), streamDetails); } @@ -408,24 +425,37 @@ public class Manager implements Closeable { // If this is the master device, other users can't send messages to this number anymore. // If this is a linked device, other users can still send messages, but this device doesn't receive them anymore. accountManager.setGcmId(Optional.absent()); + + account.setRegistered(false); + } + + public void deleteAccount() throws IOException { accountManager.deleteAccount(); account.setRegistered(false); - account.save(); } - public List getLinkedDevices() throws IOException { + public List getLinkedDevices() throws IOException { var devices = accountManager.getDevices(); account.setMultiDevice(devices.size() > 1); - account.save(); - return devices; + var identityKey = account.getIdentityKeyPair().getPrivateKey(); + return devices.stream().map(d -> { + String deviceName = d.getName(); + if (deviceName != null) { + try { + deviceName = DeviceNameUtil.decryptDeviceName(deviceName, identityKey); + } catch (IOException e) { + logger.debug("Failed to decrypt device name, maybe plain text?", e); + } + } + return new Device(d.getId(), deviceName, d.getCreated(), d.getLastSeen()); + }).collect(Collectors.toList()); } public void removeLinkedDevices(int deviceId) throws IOException { accountManager.removeDevice(deviceId); var devices = accountManager.getDevices(); account.setMultiDevice(devices.size() > 1); - account.save(); } public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException { @@ -444,7 +474,6 @@ public class Manager implements Closeable { Optional.of(account.getProfileKey().serialize()), verificationCode); account.setMultiDevice(true); - account.save(); } public void setRegistrationLockPin(Optional pin) throws IOException, UnauthenticatedResponseException { @@ -458,8 +487,7 @@ public class Manager implements Closeable { pinHelper.setRegistrationLockPin(pin.get(), masterKey); - account.setRegistrationLockPin(pin.get()); - account.setPinMasterKey(masterKey); + account.setRegistrationLockPin(pin.get(), masterKey); } else { // Remove legacy registration lock accountManager.removeRegistrationLockV1(); @@ -467,10 +495,8 @@ public class Manager implements Closeable { // Remove KBS Pin pinHelper.removeRegistrationLockPin(); - account.setRegistrationLockPin(null); - account.setPinMasterKey(null); + account.setRegistrationLockPin(null, null); } - account.save(); } void refreshPreKeys() throws IOException { @@ -520,6 +546,7 @@ public class Manager implements Closeable { account.getPassword(), account.getDeviceId(), account.getSignalProtocolStore(), + sessionLock, userAgent, account.isMultiDevice(), Optional.fromNullable(messagePipe), @@ -531,12 +558,6 @@ public class Manager implements Closeable { ServiceConfig.AUTOMATIC_NETWORK_RETRY); } - public Profile getRecipientProfile( - SignalServiceAddress address - ) { - return getRecipientProfile(resolveRecipient(address), false); - } - public Profile getRecipientProfile( RecipientId recipientId ) { @@ -548,14 +569,6 @@ public class Manager implements Closeable { Profile getRecipientProfile( RecipientId recipientId, boolean force ) { - var profileKey = account.getProfileStore().getProfileKey(recipientId); - if (profileKey == null) { - if (force) { - // retrieve profile to get identity key - retrieveEncryptedProfile(recipientId); - } - return null; - } var profile = account.getProfileStore().getProfile(recipientId); var now = new Date().getTime(); @@ -582,7 +595,18 @@ public class Manager implements Closeable { return null; } - profile = decryptProfileAndDownloadAvatar(recipientId, profileKey, encryptedProfile); + var profileKey = account.getProfileStore().getProfileKey(recipientId); + if (profileKey == null) { + profile = new Profile(new Date().getTime(), + null, + null, + null, + null, + ProfileUtils.getUnidentifiedAccessMode(encryptedProfile, null), + ProfileUtils.getCapabilities(encryptedProfile)); + } else { + profile = decryptProfileAndDownloadAvatar(recipientId, profileKey, encryptedProfile); + } account.getProfileStore().storeProfile(recipientId, profile); return profile; @@ -604,10 +628,14 @@ public class Manager implements Closeable { final var profile = profileAndCredential.getProfile(); try { - account.getIdentityKeyStore() + var newIdentity = account.getIdentityKeyStore() .saveIdentity(recipientId, new IdentityKey(Base64.getDecoder().decode(profile.getIdentityKey())), new Date()); + + if (newIdentity) { + account.getSessionStore().archiveSessions(recipientId); + } } catch (InvalidKeyException ignored) { logger.warn("Got invalid identity key in profile for {}", resolveSignalServiceAddress(recipientId).getLegacyIdentifier()); @@ -712,9 +740,10 @@ public class Manager implements Closeable { public Pair> sendGroupMessageReaction( String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, GroupId groupId ) throws IOException, InvalidNumberException, NotAGroupMemberException, GroupNotFoundException { + var targetAuthorRecipientId = canonicalizeAndResolveRecipient(targetAuthor); var reaction = new SignalServiceDataMessage.Reaction(emoji, remove, - canonicalizeAndResolveSignalServiceAddress(targetAuthor), + resolveSignalServiceAddress(targetAuthorRecipientId), targetSentTimestamp); final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction); @@ -756,10 +785,11 @@ public class Manager implements Closeable { public Pair> updateGroup( GroupId groupId, String name, List members, File avatarFile ) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException { - return sendUpdateGroupMessage(groupId, - name, - members == null ? null : getSignalServiceAddresses(members), - avatarFile); + final var membersRecipientIds = members == null ? null : getSignalServiceAddresses(members); + if (membersRecipientIds != null) { + membersRecipientIds.remove(account.getSelfRecipientId()); + } + return sendUpdateGroupMessage(groupId, name, membersRecipientIds, avatarFile); } private Pair> sendUpdateGroupMessage( @@ -947,14 +977,15 @@ public class Manager implements Closeable { } g = (GroupInfoV1) group; - if (!g.isMember(resolveRecipient(recipient))) { + final var recipientId = resolveRecipient(recipient); + if (!g.isMember(recipientId)) { throw new NotAGroupMemberException(groupId, g.name); } var messageBuilder = getGroupUpdateMessageBuilder(g); // Send group message only to the recipient who requested it - return sendMessage(messageBuilder, Set.of(resolveRecipient(recipient))); + return sendMessage(messageBuilder, Set.of(recipientId)); } private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV1 g) throws AttachmentInvalidException { @@ -1064,9 +1095,10 @@ public class Manager implements Closeable { public Pair> sendMessageReaction( String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List recipients ) throws IOException, InvalidNumberException { + var targetAuthorRecipientId = canonicalizeAndResolveRecipient(targetAuthor); var reaction = new SignalServiceDataMessage.Reaction(emoji, remove, - canonicalizeAndResolveSignalServiceAddress(targetAuthor), + resolveSignalServiceAddress(targetAuthorRecipientId), targetSentTimestamp); final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction); return sendMessage(messageBuilder, getSignalServiceAddresses(recipients)); @@ -1082,25 +1114,38 @@ public class Manager implements Closeable { for (var address : signalServiceAddresses) { handleEndSession(address); } - account.save(); throw e; } } + void renewSession(RecipientId recipientId) throws IOException { + account.getSessionStore().archiveSessions(recipientId); + if (!recipientId.equals(getSelfRecipientId())) { + sendNullMessage(recipientId); + } + } + public String getContactName(String number) throws InvalidNumberException { var contact = account.getContactStore().getContact(canonicalizeAndResolveRecipient(number)); return contact == null || contact.getName() == null ? "" : contact.getName(); } - public void setContactName(String number, String name) throws InvalidNumberException { + public void setContactName(String number, String name) throws InvalidNumberException, NotMasterDeviceException { + if (!account.isMasterDevice()) { + throw new NotMasterDeviceException(); + } final var recipientId = canonicalizeAndResolveRecipient(number); var contact = account.getContactStore().getContact(recipientId); final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); account.getContactStore().storeContact(recipientId, builder.withName(name).build()); - account.save(); } - public void setContactBlocked(String number, boolean blocked) throws InvalidNumberException { + public void setContactBlocked( + String number, boolean blocked + ) throws InvalidNumberException, NotMasterDeviceException { + if (!account.isMasterDevice()) { + throw new NotMasterDeviceException(); + } setContactBlocked(canonicalizeAndResolveRecipient(number), blocked); } @@ -1108,7 +1153,6 @@ public class Manager implements Closeable { var contact = account.getContactStore().getContact(recipientId); final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); account.getContactStore().storeContact(recipientId, builder.withBlocked(blocked).build()); - account.save(); } public void setGroupBlocked(final GroupId groupId, final boolean blocked) throws GroupNotFoundException { @@ -1119,7 +1163,6 @@ public class Manager implements Closeable { group.setBlocked(blocked); account.getGroupStore().updateGroup(group); - account.save(); } private void setExpirationTimer(RecipientId recipientId, int messageExpirationTimer) { @@ -1146,7 +1189,6 @@ public class Manager implements Closeable { var recipientId = canonicalizeAndResolveRecipient(number); setExpirationTimer(recipientId, messageExpirationTimer); sendExpirationTimerUpdate(recipientId); - account.save(); } /** @@ -1179,7 +1221,6 @@ public class Manager implements Closeable { var sticker = new Sticker(StickerPackId.deserialize(Hex.fromStringCondensed(packId)), packKey); account.getStickerStore().updateSticker(sticker); - account.save(); try { return new URI("https", @@ -1193,7 +1234,15 @@ public class Manager implements Closeable { } } - void requestSyncGroups() throws IOException { + public void requestAllSyncData() throws IOException { + requestSyncGroups(); + requestSyncContacts(); + requestSyncBlocked(); + requestSyncConfiguration(); + requestSyncKeys(); + } + + private void requestSyncGroups() throws IOException { var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.GROUPS) .build(); @@ -1205,7 +1254,7 @@ public class Manager implements Closeable { } } - void requestSyncContacts() throws IOException { + private void requestSyncContacts() throws IOException { var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS) .build(); @@ -1217,7 +1266,7 @@ public class Manager implements Closeable { } } - void requestSyncBlocked() throws IOException { + private void requestSyncBlocked() throws IOException { var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED) .build(); @@ -1229,7 +1278,7 @@ public class Manager implements Closeable { } } - void requestSyncConfiguration() throws IOException { + private void requestSyncConfiguration() throws IOException { var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION) .build(); @@ -1241,7 +1290,7 @@ public class Manager implements Closeable { } } - void requestSyncKeys() throws IOException { + private void requestSyncKeys() throws IOException { var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS) .build(); @@ -1254,11 +1303,13 @@ public class Manager implements Closeable { } private byte[] getSenderCertificate() { - // TODO support UUID capable sender certificates - // byte[] certificate = accountManager.getSenderCertificateForPhoneNumberPrivacy(); byte[] certificate; try { - certificate = accountManager.getSenderCertificate(); + if (account.isPhoneNumberShared()) { + certificate = accountManager.getSenderCertificate(); + } else { + certificate = accountManager.getSenderCertificateForPhoneNumberPrivacy(); + } } catch (IOException e) { logger.warn("Failed to get sender certificate, ignoring: {}", e.getMessage()); return null; @@ -1277,7 +1328,7 @@ public class Manager implements Closeable { final var addressesMissingUuid = new HashSet(); for (var number : numbers) { - final var resolvedAddress = canonicalizeAndResolveSignalServiceAddress(number); + final var resolvedAddress = resolveSignalServiceAddress(canonicalizeAndResolveRecipient(number)); if (resolvedAddress.getUuid().isPresent()) { signalServiceAddresses.add(resolvedAddress); } else { @@ -1311,10 +1362,20 @@ public class Manager implements Closeable { return signalServiceAddresses.stream().map(this::resolveRecipient).collect(Collectors.toSet()); } - private Map getRegisteredUsers(final Set numbersMissingUuid) throws IOException { + private RecipientId refreshRegisteredUser(RecipientId recipientId) throws IOException { + final var address = resolveSignalServiceAddress(recipientId); + if (!address.getNumber().isPresent()) { + return recipientId; + } + final var number = address.getNumber().get(); + final var uuidMap = getRegisteredUsers(Set.of(number)); + return resolveRecipientTrusted(new SignalServiceAddress(uuidMap.getOrDefault(number, null), number)); + } + + private Map getRegisteredUsers(final Set numbers) throws IOException { try { return accountManager.getRegisteredUsers(ServiceConfig.getIasKeyStore(), - numbersMissingUuid, + numbers, serviceEnvironmentConfig.getCdsMrenclave()); } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) { throw new IOException(e); @@ -1346,10 +1407,12 @@ public class Manager implements Closeable { for (var r : result) { if (r.getIdentityFailure() != null) { - account.getIdentityKeyStore(). - saveIdentity(resolveRecipient(r.getAddress()), - r.getIdentityFailure().getIdentityKey(), - new Date()); + final var recipientId = resolveRecipient(r.getAddress()); + final var newIdentity = account.getIdentityKeyStore() + .saveIdentity(recipientId, r.getIdentityFailure().getIdentityKey(), new Date()); + if (newIdentity) { + account.getSessionStore().archiveSessions(recipientId); + } } } @@ -1366,7 +1429,7 @@ public class Manager implements Closeable { final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0; messageBuilder.withExpiration(expirationTime); message = messageBuilder.build(); - results.add(sendMessage(resolveSignalServiceAddress(recipientId), message)); + results.add(sendMessage(recipientId, message)); } return new Pair<>(timestamp, results); } @@ -1376,7 +1439,6 @@ public class Manager implements Closeable { handleEndSession(recipient); } } - account.save(); } } @@ -1387,27 +1449,24 @@ public class Manager implements Closeable { messageBuilder.withTimestamp(timestamp); getOrCreateMessagePipe(); getOrCreateUnidentifiedMessagePipe(); - try { - final var recipientId = account.getSelfRecipientId(); + final var recipientId = account.getSelfRecipientId(); - final var contact = account.getContactStore().getContact(recipientId); - final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0; - messageBuilder.withExpiration(expirationTime); + final var contact = account.getContactStore().getContact(recipientId); + final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0; + messageBuilder.withExpiration(expirationTime); - var message = messageBuilder.build(); - final var result = sendSelfMessage(message); - return new Pair<>(timestamp, result); - } finally { - account.save(); - } + var message = messageBuilder.build(); + final var result = sendSelfMessage(message); + return new Pair<>(timestamp, result); } private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException { var messageSender = createMessageSender(); - var recipient = account.getSelfAddress(); + var recipientId = account.getSelfRecipientId(); - final var unidentifiedAccess = unidentifiedAccessHelper.getAccessFor(resolveRecipient(recipient)); + final var unidentifiedAccess = unidentifiedAccessHelper.getAccessFor(recipientId); + var recipient = resolveSignalServiceAddress(recipientId); var transcript = new SentTranscriptMessage(Optional.of(recipient), message.getTimestamp(), message, @@ -1429,33 +1488,50 @@ public class Manager implements Closeable { } private SendMessageResult sendMessage( - SignalServiceAddress address, SignalServiceDataMessage message + RecipientId recipientId, SignalServiceDataMessage message ) throws IOException { var messageSender = createMessageSender(); + final var address = resolveSignalServiceAddress(recipientId); try { - return messageSender.sendMessage(address, - unidentifiedAccessHelper.getAccessFor(resolveRecipient(address)), - message); + try { + return messageSender.sendMessage(address, unidentifiedAccessHelper.getAccessFor(recipientId), message); + } catch (UnregisteredUserException e) { + final var newRecipientId = refreshRegisteredUser(recipientId); + return messageSender.sendMessage(resolveSignalServiceAddress(newRecipientId), + unidentifiedAccessHelper.getAccessFor(newRecipientId), + message); + } } catch (UntrustedIdentityException e) { return SendMessageResult.identityFailure(address, e.getIdentityKey()); } } - private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, org.whispersystems.libsignal.UntrustedIdentityException { - var cipher = new SignalServiceCipher(account.getSelfAddress(), - account.getSignalProtocolStore(), - certificateValidator); + private SendMessageResult sendNullMessage(RecipientId recipientId) throws IOException { + var messageSender = createMessageSender(); + + final var address = resolveSignalServiceAddress(recipientId); try { - return cipher.decrypt(envelope); - } catch (ProtocolUntrustedIdentityException e) { - if (e.getCause() instanceof org.whispersystems.libsignal.UntrustedIdentityException) { - throw (org.whispersystems.libsignal.UntrustedIdentityException) e.getCause(); + try { + return messageSender.sendNullMessage(address, unidentifiedAccessHelper.getAccessFor(recipientId)); + } catch (UnregisteredUserException e) { + final var newRecipientId = refreshRegisteredUser(recipientId); + final var newAddress = resolveSignalServiceAddress(newRecipientId); + return messageSender.sendNullMessage(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId)); } - throw new AssertionError(e); + } catch (UntrustedIdentityException e) { + return SendMessageResult.identityFailure(address, e.getIdentityKey()); } } + private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, ProtocolUntrustedIdentityException { + var cipher = new SignalServiceCipher(account.getSelfAddress(), + account.getSignalProtocolStore(), + sessionLock, + certificateValidator); + return cipher.decrypt(envelope); + } + private void handleEndSession(RecipientId recipientId) { account.getSessionStore().deleteAllSessions(recipientId); } @@ -1658,11 +1734,11 @@ public class Manager implements Closeable { private void storeProfileKeysFromMembers(final DecryptedGroup group) { for (var member : group.getMembersList()) { - final var address = resolveRecipient(new SignalServiceAddress(UuidUtil.parseOrThrow(member.getUuid() - .toByteArray()), null)); + final var uuid = UuidUtil.parseOrThrow(member.getUuid().toByteArray()); + final var recipientId = account.getRecipientStore().resolveRecipient(uuid); try { account.getProfileStore() - .storeProfileKey(address, new ProfileKey(member.getProfileKey().toByteArray())); + .storeProfileKey(recipientId, new ProfileKey(member.getProfileKey().toByteArray())); } catch (InvalidInputException ignored) { } } @@ -1697,10 +1773,10 @@ public class Manager implements Closeable { if (!envelope.isReceipt()) { try { content = decryptMessage(envelope); - } catch (org.whispersystems.libsignal.UntrustedIdentityException e) { + } catch (ProtocolUntrustedIdentityException e) { if (!envelope.hasSource()) { - final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) e) - .getName()); + final var identifier = e.getSender(); + final var recipientId = resolveRecipient(identifier); try { account.getMessageCache().replaceSender(cachedMessage, recipientId); } catch (IOException ioException) { @@ -1715,7 +1791,6 @@ public class Manager implements Closeable { } actions = handleMessage(envelope, content, ignoreAttachments); } - account.save(); handler.handleMessage(envelope, content, null); cachedMessage.delete(); return actions; @@ -1763,7 +1838,6 @@ public class Manager implements Closeable { logger.warn("Message action failed.", e); } } - account.save(); queuedActions.clear(); queuedActions = null; } @@ -1778,6 +1852,7 @@ public class Manager implements Closeable { if (envelope.hasSource()) { // Store uuid if we don't have it already + // address/uuid in envelope is sent by server resolveRecipientTrusted(envelope.getSourceAddress()); } final var notAGroupMember = isNotAGroupMember(envelope, content); @@ -1787,7 +1862,17 @@ public class Manager implements Closeable { } catch (Exception e) { exception = e; } + if (!envelope.hasSource() && content != null) { + // Store uuid if we don't have it already + // address/uuid is validated by unidentified sender certificate + resolveRecipientTrusted(content.getSender()); + } var actions = handleMessage(envelope, content, ignoreAttachments); + if (exception instanceof ProtocolInvalidMessageException) { + final var sender = resolveRecipient(((ProtocolInvalidMessageException) exception).getSender()); + logger.debug("Received invalid message, queuing renew session action."); + actions.add(new RenewSessionAction(sender)); + } if (hasCaughtUpWithOldMessages) { for (var action : actions) { try { @@ -1803,7 +1888,6 @@ public class Manager implements Closeable { queuedActions.addAll(actions); } } - account.save(); if (isMessageBlocked(envelope, content)) { logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp()); } else if (notAGroupMember) { @@ -1812,9 +1896,9 @@ public class Manager implements Closeable { handler.handleMessage(envelope, content, exception); } if (cachedMessage[0] != null) { - if (exception instanceof org.whispersystems.libsignal.UntrustedIdentityException) { - final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) exception) - .getName()); + if (exception instanceof ProtocolUntrustedIdentityException) { + final var identifier = ((ProtocolUntrustedIdentityException) exception).getSender(); + final var recipientId = resolveRecipient(identifier); queuedActions.add(new RetrieveProfileAction(recipientId)); if (!envelope.hasSource()) { try { @@ -2529,14 +2613,6 @@ public class Manager implements Closeable { theirIdentityKey); } - @Deprecated - public SignalServiceAddress canonicalizeAndResolveSignalServiceAddress(String identifier) throws InvalidNumberException { - var canonicalizedNumber = UuidUtil.isUuid(identifier) - ? identifier - : PhoneNumberFormatter.formatNumber(identifier, account.getUsername()); - return resolveSignalServiceAddress(canonicalizedNumber); - } - @Deprecated public SignalServiceAddress resolveSignalServiceAddress(String identifier) { var address = Utils.getSignalServiceAddressFromIdentifier(identifier);