X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/05abb3f9f6294677d2de97a9d88c8e9de04c10ec..2ef59d692ae92615105c6aa2603677a3fa584200:/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 5d0e7d3d..60263dd9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -34,9 +34,10 @@ import org.asamk.signal.manager.storage.contacts.ContactInfo; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.groups.GroupInfoV2; +import org.asamk.signal.manager.storage.identities.IdentityInfo; import org.asamk.signal.manager.storage.messageCache.CachedMessage; import org.asamk.signal.manager.storage.profiles.SignalProfile; -import org.asamk.signal.manager.storage.protocol.IdentityInfo; +import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.stickers.Sticker; import org.asamk.signal.manager.util.AttachmentUtils; import org.asamk.signal.manager.util.IOUtils; @@ -234,8 +235,6 @@ public class Manager implements Closeable { clientZkProfileOperations, ServiceConfig.AUTOMATIC_NETWORK_RETRY); - this.account.setResolver(this::resolveSignalServiceAddress); - this.unidentifiedAccessHelper = new UnidentifiedAccessHelper(account::getProfileKey, account.getProfileStore()::getProfileKey, this::getRecipientProfile, @@ -263,7 +262,7 @@ public class Manager implements Closeable { } private IdentityKeyPair getIdentityKeyPair() { - return account.getSignalProtocolStore().getIdentityKeyPair(); + return account.getIdentityKeyPair(); } public int getDeviceId() { @@ -336,7 +335,7 @@ public class Manager implements Closeable { public void updateAccountAttributes() throws IOException { accountManager.setAccountAttributes(null, - account.getSignalProtocolStore().getLocalRegistrationId(), + account.getLocalRegistrationId(), true, // set legacy pin only if no KBS master key is set account.getPinMasterKey() == null ? account.getRegistrationLockPin() : null, @@ -482,7 +481,6 @@ public class Manager implements Closeable { var records = KeyUtils.generatePreKeyRecords(offset, ServiceConfig.PREKEY_BATCH_SIZE); account.addPreKeys(records); - account.save(); return records; } @@ -492,7 +490,6 @@ public class Manager implements Closeable { var record = KeyUtils.generateSignedPreKeyRecord(identityKeyPair, signedPreKeyId); account.addSignedPreKey(record); - account.save(); return record; } @@ -1225,17 +1222,7 @@ public class Manager implements Closeable { private void sendSyncMessage(SignalServiceSyncMessage message) throws IOException, UntrustedIdentityException { var messageSender = createMessageSender(); - try { - messageSender.sendMessage(message, unidentifiedAccessHelper.getAccessForSync()); - } catch (UntrustedIdentityException e) { - if (e.getIdentityKey() != null) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); - } - throw e; - } + messageSender.sendMessage(message, unidentifiedAccessHelper.getAccessForSync()); } private Collection getSignalServiceAddresses(Collection numbers) throws InvalidNumberException { @@ -1305,22 +1292,8 @@ public class Manager implements Closeable { unidentifiedAccessHelper.getAccessFor(recipients), isRecipientUpdate, message); - for (var r : result) { - if (r.getIdentityFailure() != null) { - account.getSignalProtocolStore() - .saveIdentity(r.getAddress(), - r.getIdentityFailure().getIdentityKey(), - TrustLevel.UNTRUSTED); - } - } return new Pair<>(timestamp, result); } catch (UntrustedIdentityException e) { - if (e.getIdentityKey() != null) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); - } return new Pair<>(timestamp, List.of()); } } else { @@ -1390,12 +1363,6 @@ public class Manager implements Closeable { false, System.currentTimeMillis() - startTime); } catch (UntrustedIdentityException e) { - if (e.getIdentityKey() != null) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); - } return SendMessageResult.identityFailure(recipient, e.getIdentityKey()); } } @@ -1408,12 +1375,6 @@ public class Manager implements Closeable { try { return messageSender.sendMessage(address, unidentifiedAccessHelper.getAccessFor(address), message); } catch (UntrustedIdentityException e) { - if (e.getIdentityKey() != null) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); - } return SendMessageResult.identityFailure(address, e.getIdentityKey()); } } @@ -1426,22 +1387,14 @@ public class Manager implements Closeable { return cipher.decrypt(envelope); } catch (ProtocolUntrustedIdentityException e) { if (e.getCause() instanceof org.whispersystems.libsignal.UntrustedIdentityException) { - var identityException = (org.whispersystems.libsignal.UntrustedIdentityException) e.getCause(); - final var untrustedIdentity = identityException.getUntrustedIdentity(); - if (untrustedIdentity != null) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(identityException.getName()), - untrustedIdentity, - TrustLevel.UNTRUSTED); - } - throw identityException; + throw (org.whispersystems.libsignal.UntrustedIdentityException) e.getCause(); } throw new AssertionError(e); } } private void handleEndSession(SignalServiceAddress source) { - account.getSignalProtocolStore().deleteAllSessions(source); + account.getSessionStore().deleteAllSessions(source.getIdentifier()); } private List handleSignalServiceDataMessage( @@ -1676,6 +1629,15 @@ public class Manager implements Closeable { try { content = decryptMessage(envelope); } catch (org.whispersystems.libsignal.UntrustedIdentityException e) { + if (!envelope.hasSource()) { + final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) e) + .getName()); + try { + account.getMessageCache().replaceSender(cachedMessage, recipientId); + } catch (IOException ioException) { + logger.warn("Failed to move cached message to recipient folder: {}", ioException.getMessage()); + } + } return; } catch (Exception er) { // All other errors are not recoverable, so delete the cached message @@ -1718,8 +1680,11 @@ public class Manager implements Closeable { final CachedMessage[] cachedMessage = {null}; try { var result = messagePipe.readOrEmpty(timeout, unit, envelope1 -> { + final var recipientId = envelope1.hasSource() + ? resolveRecipient(envelope1.getSourceIdentifier()) + : null; // store message on disk, before acknowledging receipt to the server - cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1); + cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1, recipientId); }); if (result.isPresent()) { envelope = result.get(); @@ -1750,8 +1715,7 @@ public class Manager implements Closeable { if (envelope.hasSource()) { // Store uuid if we don't have it already - var source = envelope.getSourceAddress(); - resolveSignalServiceAddress(source); + resolveRecipientTrusted(envelope.getSourceAddress()); } if (!envelope.isReceipt()) { try { @@ -1783,8 +1747,19 @@ public class Manager implements Closeable { } else { handler.handleMessage(envelope, content, exception); } - if (!(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException)) { - if (cachedMessage[0] != null) { + if (cachedMessage[0] != null) { + if (exception instanceof org.whispersystems.libsignal.UntrustedIdentityException) { + if (!envelope.hasSource()) { + final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) exception) + .getName()); + try { + cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId); + } catch (IOException ioException) { + logger.warn("Failed to move cached message to recipient folder: {}", + ioException.getMessage()); + } + } + } else { cachedMessage[0].delete(); } } @@ -2006,8 +1981,8 @@ public class Manager implements Closeable { } if (c.getVerified().isPresent()) { final var verifiedMessage = c.getVerified().get(); - account.getSignalProtocolStore() - .setIdentityTrustLevel(verifiedMessage.getDestination(), + account.getIdentityKeyStore() + .setIdentityTrustLevel(resolveRecipientTrusted(verifiedMessage.getDestination()), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); } @@ -2042,8 +2017,8 @@ public class Manager implements Closeable { } if (syncMessage.getVerified().isPresent()) { final var verifiedMessage = syncMessage.getVerified().get(); - account.getSignalProtocolStore() - .setIdentityTrustLevel(resolveSignalServiceAddress(verifiedMessage.getDestination()), + account.getIdentityKeyStore() + .setIdentityTrustLevel(resolveRecipientTrusted(verifiedMessage.getDestination()), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); } @@ -2285,7 +2260,8 @@ public class Manager implements Closeable { var out = new DeviceContactsOutputStream(fos); for (var record : account.getContactStore().getContacts()) { VerifiedMessage verifiedMessage = null; - var currentIdentity = account.getSignalProtocolStore().getIdentity(record.getAddress()); + var currentIdentity = account.getIdentityKeyStore() + .getIdentity(resolveRecipientTrusted(record.getAddress())); if (currentIdentity != null) { verifiedMessage = new VerifiedMessage(record.getAddress(), currentIdentity.getIdentityKey(), @@ -2397,11 +2373,12 @@ public class Manager implements Closeable { } public List getIdentities() { - return account.getSignalProtocolStore().getIdentities(); + return account.getIdentityKeyStore().getIdentities(); } public List getIdentities(String number) throws InvalidNumberException { - return account.getSignalProtocolStore().getIdentities(canonicalizeAndResolveSignalServiceAddress(number)); + final var identity = account.getIdentityKeyStore().getIdentity(canonicalizeAndResolveRecipient(number)); + return identity == null ? List.of() : List.of(identity); } /** @@ -2411,8 +2388,10 @@ public class Manager implements Closeable { * @param fingerprint Fingerprint */ public boolean trustIdentityVerified(String name, byte[] fingerprint) throws InvalidNumberException { - var address = canonicalizeAndResolveSignalServiceAddress(name); - return trustIdentity(address, (identityKey) -> Arrays.equals(identityKey.serialize(), fingerprint)); + var recipientId = canonicalizeAndResolveRecipient(name); + return trustIdentity(recipientId, + identityKey -> Arrays.equals(identityKey.serialize(), fingerprint), + TrustLevel.TRUSTED_VERIFIED); } /** @@ -2422,72 +2401,43 @@ public class Manager implements Closeable { * @param safetyNumber Safety number */ public boolean trustIdentityVerifiedSafetyNumber(String name, String safetyNumber) throws InvalidNumberException { - var address = canonicalizeAndResolveSignalServiceAddress(name); - return trustIdentity(address, (identityKey) -> safetyNumber.equals(computeSafetyNumber(address, identityKey))); + var recipientId = canonicalizeAndResolveRecipient(name); + var address = account.getRecipientStore().resolveServiceAddress(recipientId); + return trustIdentity(recipientId, + identityKey -> safetyNumber.equals(computeSafetyNumber(address, identityKey)), + TrustLevel.TRUSTED_VERIFIED); } - private boolean trustIdentity(SignalServiceAddress address, Function verifier) { - var ids = account.getSignalProtocolStore().getIdentities(address); - if (ids == null) { - return false; - } - - IdentityInfo foundIdentity = null; + /** + * Trust all keys of this identity without verification + * + * @param name username of the identity + */ + public boolean trustIdentityAllKeys(String name) throws InvalidNumberException { + var recipientId = canonicalizeAndResolveRecipient(name); + return trustIdentity(recipientId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED); + } - for (var id : ids) { - if (verifier.apply(id.getIdentityKey())) { - foundIdentity = id; - break; - } + private boolean trustIdentity( + RecipientId recipientId, Function verifier, TrustLevel trustLevel + ) { + var identity = account.getIdentityKeyStore().getIdentity(recipientId); + if (identity == null) { + return false; } - if (foundIdentity == null) { + if (!verifier.apply(identity.getIdentityKey())) { return false; } - account.getSignalProtocolStore() - .setIdentityTrustLevel(address, foundIdentity.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); + account.getIdentityKeyStore().setIdentityTrustLevel(recipientId, identity.getIdentityKey(), trustLevel); try { - sendVerifiedMessage(address, foundIdentity.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); + var address = account.getRecipientStore().resolveServiceAddress(recipientId); + sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel); } catch (IOException | UntrustedIdentityException e) { logger.warn("Failed to send verification sync message: {}", e.getMessage()); } - // Successfully trusted the new identity, now remove all other identities for that number - for (var id : ids) { - if (id == foundIdentity) { - continue; - } - account.getSignalProtocolStore().removeIdentity(address, id.getIdentityKey()); - } - - account.save(); - return true; - } - - /** - * Trust all keys of this identity without verification - * - * @param name username of the identity - */ - public boolean trustIdentityAllKeys(String name) { - var address = resolveSignalServiceAddress(name); - var ids = account.getSignalProtocolStore().getIdentities(address); - if (ids == null) { - return false; - } - for (var id : ids) { - if (id.getTrustLevel() == TrustLevel.UNTRUSTED) { - account.getSignalProtocolStore() - .setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_UNVERIFIED); - try { - sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_UNVERIFIED); - } catch (IOException | UntrustedIdentityException e) { - logger.warn("Failed to send verification sync message: {}", e.getMessage()); - } - } - } - account.save(); return true; } @@ -2501,6 +2451,7 @@ public class Manager implements Closeable { theirIdentityKey); } + @Deprecated public SignalServiceAddress canonicalizeAndResolveSignalServiceAddress(String identifier) throws InvalidNumberException { var canonicalizedNumber = UuidUtil.isUuid(identifier) ? identifier @@ -2508,12 +2459,14 @@ public class Manager implements Closeable { return resolveSignalServiceAddress(canonicalizedNumber); } + @Deprecated public SignalServiceAddress resolveSignalServiceAddress(String identifier) { var address = Utils.getSignalServiceAddressFromIdentifier(identifier); return resolveSignalServiceAddress(address); } + @Deprecated public SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address) { if (address.matches(account.getSelfAddress())) { return account.getSelfAddress(); @@ -2522,6 +2475,32 @@ public class Manager implements Closeable { return account.getRecipientStore().resolveServiceAddress(address); } + public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) { + return account.getRecipientStore().resolveServiceAddress(recipientId); + } + + public RecipientId canonicalizeAndResolveRecipient(String identifier) throws InvalidNumberException { + var canonicalizedNumber = UuidUtil.isUuid(identifier) + ? identifier + : PhoneNumberFormatter.formatNumber(identifier, account.getUsername()); + + return resolveRecipient(canonicalizedNumber); + } + + private RecipientId resolveRecipient(final String identifier) { + var address = Utils.getSignalServiceAddressFromIdentifier(identifier); + + return resolveRecipient(address); + } + + public RecipientId resolveRecipient(SignalServiceAddress address) { + return account.getRecipientStore().resolveRecipientUntrusted(address); + } + + private RecipientId resolveRecipientTrusted(SignalServiceAddress address) { + return account.getRecipientStore().resolveRecipient(address); + } + @Override public void close() throws IOException { close(true);