*/
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;
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;
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;
throw new NotRegisteredException();
}
- var account = SignalAccount.load(pathConfig.getDataPath(), username);
+ var account = SignalAccount.load(pathConfig.getDataPath(), username, true);
if (!account.isRegistered()) {
throw new NotRegisteredException();
}
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
account.setRegistered(false);
}
- public List<DeviceInfo> getLinkedDevices() throws IOException {
+ public List<Device> getLinkedDevices() throws IOException {
var devices = accountManager.getDevices();
account.setMultiDevice(devices.size() > 1);
- 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 {
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();
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;
}
}
+ 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();
}
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;
}
}
+ private SendMessageResult sendNullMessage(RecipientId recipientId) throws IOException {
+ var messageSender = createMessageSender();
+
+ final var address = resolveSignalServiceAddress(recipientId);
+ try {
+ 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));
+ }
+ } 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(),
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);
} 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 {